1

My controller receives a JSON string inside params which looks like following:

{"serialized"=>"{\"key\":{\"subkey1\":"value",\"subkey2\":"value"}"}

In my controller I try the following:

JSON.parse(params[:serialized], symbolize_names: true)

Which returns:

{:"key"=>{:subkey1=>"value", :subkey2=>"value"}

All the nested subkeys were symbolized; the key was symbolized in a weird way so it is not responding to hash[key], but does to hash["key"].

If I go through the Rails stack:

ActiveSupport::JSON.decode(params[:serialized]).symbolize_keys

I get back the following:

{:"key"=>{"subkey1"=>"value", "subkey2"=>"value"}

Almost same as the first one except for nested keys; they are not being symbolized.

I've even tried looping through hash trying to symbolize the keys manually; with no success though:

Hash[params[:serialized]{ |k, v| [k.to_sym, v] }] # returns {:"key"=>{"subkey1"=>"value", "subkey2"=>"value2"}

Why is this happening? Why is the key symbolized as :"key" instead of :key?

UPD Removed last line (How could I possibly fix that since I need my hash to answer to hash[key] and not hash["key"].) so the question looks less pragmatic and more theoretic.

3
  • FWIW :key == :"key" #=> true Commented Apr 29, 2013 at 15:42
  • Why are you expecting hash[key] instead of hash[:key] to be possible? Commented Apr 29, 2013 at 15:50
  • @sawa I'd say it's rather unimportant; more important is why is it happening? Commented Apr 29, 2013 at 17:41

4 Answers 4

2

try on rails console.

require 'json'
string = {"serialized"=>"{\"key\":{\"subkey1\":"value",\"subkey2\":"value"}"}
hash = JSON.parse string
Sign up to request clarification or add additional context in comments.

Comments

1

I agree with what @boulder said above. But since, Hash[params[:serialized].symbolized_keys.map{ |k, v| [k.to_sym, v.symbolize_keys] }] is for symbolizing till 1 level. I won't really go for this, ever.

This is probably different from what you are asking for, but accessing hash's key/values in Rails, is usually suited like hash.key instead of hash[:key] or hash['key'].
This is the primary convention, and is the reason, how one can access fields like @user.name etc.

Rails implements this by OpenStruct. To use it, you can do:

@foo = ActiveSupport::JSON.decode(params[:serialized]).symbolize_keys
obj = OpenStruct.new(@foo)
obj.key #=> {"subkey1"=>"value", "subkey2"=>"value"}

But again, OpenStruct creates the object till one level for accessing with .key instead of ':key'. To aid this, we have Recursive OpenStruct, which does the job perfectly.

This is the way, I personally feel, you should work in Rails in scenario like this(if arrives).

Comments

0

First of all, :key and :"key" are two ways to express the exact same thing. In fact if you do:

> :key == :"key" 
=> true

so given a hash such as

h = {:"key" => "value"}
h[:key]
=> "value"

Secondly, if you have nested hashes, you don't only want to symbolize the keys manually, you also want to symbolize the keys in the values

Hash[params[:serialized].symbolized_keys.map{ |k, v| [k.to_sym, v.symbolize_keys] }]

Of course, you need something more elaborate if you have more than one level of 'nestesness'

3 Comments

The manual altering of keys was there just as an example. I know that :key == :"key". I've asked why is this happening; i.e. half of keys are converted to :key and the other half is converted to :"key". symbolize_keys just calls VARIABLE.to_sym which evaluates into :VARIABLE. So where is the magic happening?
I see, I understood your question as being more pragmatic, since you end with ' How could I possibly fix that since I need my hash to answer to hash[key] and not hash["key"]'? To answer your question, you'll get the :"key" notation when you're creating symbols that cannot be represented using the :xxx notation. For example "key\ ".to_sym returns :"key " while "key".to_sym returns :key
Yes, thats correct - "key\ " returns :"key " but notice the space there; In first case there was no space returned in :"key:
0

Old topic, but for those that might fall on it, Rails provides the method deep_symbolize_keys that seems to do exactly what op needs after parsing json :

hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }

hash.deep_symbolize_keys
# => {:person=>{:name=>"Rob", :age=>"28"}}

Source: https://apidock.com/rails/Hash/deep_symbolize_keys

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.