Reputation: 1301
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
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
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
Reputation: 2497
try on rails console.
require 'json'
string = {"serialized"=>"{\"key\":{\"subkey1\":"value",\"subkey2\":"value"}"}
hash = JSON.parse string
Upvotes: 2
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