Ruby JSON parse changes Hash keys
Solution 1
In short, no. Think about it this way, storing symbols in JSON is the same as storing strings in JSON. So you cannot possibly distinguish between the two when it comes to parsing the JSON string. You can of course convert the string keys back into symbols, or in fact even build a class to interact with JSON which does this automagically, but I would recommend just using strings.
But, just for the sake of it, here are the answers to this question the previous times it's been asked:
what is the best way to convert a json formatted key value pair to ruby hash with symbol as key?
ActiveSupport::JSON decode hash losing symbols
Or perhaps a HashWithIndifferentAccess
Solution 2
The JSON generator converts symbols to strings because JSON does not support symbols. Since JSON keys are all strings, parsing a JSON document will produce a Ruby hash with string keys by default.
You can tell the parser to use symbols instead of strings by using the symbolize_names
option.
Example:
original_hash = {:info => [{:from => "Ryan Bates", :message => "sup bra", :time => "04:35 AM"}]}
serialized = JSON.generate(original_hash)
new_hash = JSON.parse(serialized, {:symbolize_names => true})
new_hash[:info]
#=> [{:from=>"Ryan Bates", :message=>"sup bra", :time=>"04:35 AM"}]
Reference: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/json/rdoc/JSON.html#method-i-parse
Solution 3
I solved my similar issue with calling the with_indifferent_access method on it
Here I have a json string and we can assign it to variable s
s = "{\"foo\":{\"bar\":\"cool\"}}"
So now I can parse the data with the JSON class and assign it to h
h = JSON.parse(s).with_indifferent_access
This will produce a hash that can accept a string or a symbol as the key
h[:foo]["bar"]
#=> "cool"
Solution 4
- Use ActiveSupport::JSON.decode, it will allow you to swap json parsers easier
- Use ActiveSupport::JSON.decode(my_json, symbolize_names: true)
This will recursively symbolize all keys in the hash.
(confirmed on ruby 2.0)
Solution 5
It's possible to modify all the keys in a hash to convert them from a string to a symbol:
symbol_hash = Hash[obj.map{ |k,v| [k.to_sym, v] }]
puts symbol_hash[:info]
# => {"from"=>"Ryan Bates", "message"=>"sup bra", "time"=>"04:35 AM"}
Unfortunately that doesn't work for the hash nested inside the array. You can, however, write a little recursive method that converts all hash keys:
def symbolize_keys(obj)
#puts obj.class # Useful for debugging
return obj.collect { |a| symbolize_keys(a) } if obj.is_a?(Array)
return obj unless obj.is_a?(Hash)
return Hash[obj.map{ |k,v| [k.to_sym, symbolize_keys(v)] }]
end
symbol_hash = symbolize_keys(hash)
puts symbol_hash[:info]
# => {:from=>"Ryan Bates", :message=>"sup bra", :time=>"04:35 AM"}
LanguagesNamedAfterCofee
Updated on July 05, 2022Comments
-
LanguagesNamedAfterCofee almost 2 years
Lets say I have this Hash:
{ :info => [ { :from => "Ryan Bates", :message => "sup bra", :time => "04:35 AM" } ] }
I can call the info array by doing
hash[:info]
.Now when I turn this into JSON (JSON.generate), and then parse it (JSON.parse), I get this hash:
{ "info" => [ { "from" => "Ryan Bates", "message" => "sup bra", "time" => "04:35 AM" } ] }
Now if I use
hash[:info]
it returnsnil
, but not if I usehash["info"]
.Why is this? And is there anyway to fix this incompatibility (besides using string keys from the start)?
-
LanguagesNamedAfterCofee over 12 yearsThanks for the links, but like you said, I'll just use strings for the keys
-
Boushley over 11 yearsAnd I think it's :symbolize_keys now (rubydoc.info/gems/multi_json/1.3.2/MultiJson)
-
wyattisimo over 11 years@Boushley: No, it's still
symbolize_names
. My answer references the JSON engine that ships with the Ruby standard library. You're referencing a third-party gem. -
nruth almost 10 yearsseems to be removed in rails 4.1? edgeguides.rubyonrails.org/… and api.rubyonrails.org/classes/ActiveSupport/JSON.html states option support removed
-
Mark about 8 yearsIs
with_indifferent_access
added toHash
via something in the Rails ecosystem? -
aaron.v about 8 years@Mark you are correct. Here is a link api.rubyonrails.org/v4.2.5/classes/ActiveSupport/…