Ruby JSON parse changes Hash keys

Ruby on-RailsRubyJsonHash

Ruby on-Rails Problem Overview


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 returns nil, but not if I use hash["info"].

Why is this? And is there anyway to fix this incompatibility (besides using string keys from the start)?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

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 2 - Ruby on-Rails

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:

https://stackoverflow.com/questions/1732001/what-is-the-best-way-to-convert-a-json-formated-key-value-pair-to-ruby-hash-with

https://stackoverflow.com/questions/5195364/activesupportjson-decode-hash-losing-symbols

Or perhaps a HashWithIndifferentAccess

Solution 3 - Ruby on-Rails

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 - Ruby on-Rails

  1. Use ActiveSupport::JSON.decode, it will allow you to swap json parsers easier
  2. 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 - Ruby on-Rails

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"}

Solution 6 - Ruby on-Rails

You can't use that option like this

ActiveSupport::JSON.decode(str_json, symbolize_names: true)

> In Rails 4.1 or later, ActiveSupport::JSON.decode no longer accepts > an options hash for MultiJSON. MultiJSON reached its end of life and > has been removed.

You can use symbolize_keys to handle it. Warning: It works only for JSON strings parsed to hash.

ActiveSupport::JSON.decode(str_json).symbolize_keys

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLanguagesNamedAfterCofeeView Question on Stackoverflow
Solution 1 - Ruby on-RailswyattisimoView Answer on Stackoverflow
Solution 2 - Ruby on-RailsLee JarvisView Answer on Stackoverflow
Solution 3 - Ruby on-Railsaaron.vView Answer on Stackoverflow
Solution 4 - Ruby on-RailsJdfreeflyView Answer on Stackoverflow
Solution 5 - Ruby on-RailsKathrynView Answer on Stackoverflow
Solution 6 - Ruby on-Railsrails_idView Answer on Stackoverflow