When I assign an Array or Hash to an attribute of a Mongo document, it gets properly serialized except for Symbols when they are used as Hash keys. Simple example:
irb>MyMongoModel.create :some_attr => {:a => [:b,:c]}
=> #<MyMongoModel _id: 4d861c34c865a1f06a000001, some_attr: {:a=>[:b, :c]}>
irb>MyMongoModel.last
=> #<MyMongoModel _id: 4d861c34c865a1f06a000001, some_attr: {"a"=>[:b, :c]}>
Please, note that some_attr
is retrieved as {"a"=>[:b, :c]}
, not as
{:a=>[:b, :c]}
This also happens for nested Hashes (e.g., inside of Arrays or other Hashes). Is there a way to preserve Symbols in such cases?
Solution
I'm using YAML to manually serialize some_attr
- YAML.dump (or Object#to_yaml
) before storing, and YAML::load after reading the attribute. YAML preserves the serialized object better. ActiveRecord is using YAML to implement its serialize
class method on ActiveRecord::Base
.
Symbols are most commonly used as placeholders in Hashes. We have used symbols already in Rails, for example the Rails params hash associates any the values of any parameters passed in by the user (from a form or in the url) with a symbol representing the name of that value.
According to the specification, strings that are used as a key to a hash are duplicated and frozen. Other mutable objects do not seem to have such special consideration. For example, with an array key, the following is possible. On the other hand, a similar thing cannot be done with a string key.
Converting between symbols to strings is easy - use the . to_s method. Converting string to symbols is equally easy - use the . to_sym method.
More than likely this has to do with the ORM you are using to provide the persistance layer for the model. You can probably wrap some_attr
with a method that returns it in the form of a HashWithIndifferentAccess
which you can then access with either strings or arrays. Since you are using Rails, this functionality can be activated by calling the with_indifferent_access
method on the Hash object. (If you have an array of Hash objects, you'll need to call it on each one of course) The method will return the same hash, but then symbol lookups will work.
From your code:
new_hash = MyMongoModel.last.some_attr.with_indifferent_access
new_hash[:a] # Will return the same as new_hash['a']
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With