Ruslan
Ruslan

Reputation: 1301

Ruby/Rails - issues with JSON.parse/JSON.decode

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.

Upvotes: 1

Views: 4661

Answers (4)

kiddorails
kiddorails

Reputation: 13014

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).

Upvotes: 1

Mene
Mene

Reputation: 354

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

Upvotes: 0

Ashwini
Ashwini

Reputation: 2497

try on rails console.

require 'json'
string = {"serialized"=>"{\"key\":{\"subkey1\":"value",\"subkey2\":"value"}"}
hash = JSON.parse string

Upvotes: 2

boulder
boulder

Reputation: 3266

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'

Upvotes: 0

Related Questions