Ruby JSON解析改变了Hash密钥

时间:2011-09-25 05:11:54

标签: ruby-on-rails ruby json hash

让我说我有这个哈希:

{
  :info => [
    {
        :from => "Ryan Bates",
        :message => "sup bra",
        :time => "04:35 AM"
    }
  ]
}

我可以通过hash[:info]调用info数组。

现在,当我将其转换为JSON(JSON.generate),然后解析它(JSON.parse)时,我得到了这个哈希:

{
  "info" => [
    {
        "from" => "Ryan Bates",
        "message" => "sup bra",
        "time" => "04:35 AM"
    }
  ]
}

现在,如果我使用hash[:info],则会返回nil,但如果我使用hash["info"]则不会。

这是为什么?无论如何要修复这种不兼容性(除了从头开始使用字符串键)?

6 个答案:

答案 0 :(得分:133)

JSON生成器将符号转换为字符串,因为JSON不支持符号。由于JSON键都是字符串,因此解析JSON文档将默认生成带字符串键的Ruby哈希。

您可以使用symbolize_names选项告诉解析器使用符号而不是字符串。

实施例:

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

参考:http://www.ruby-doc.org/stdlib-1.9.3/libdoc/json/rdoc/JSON.html#method-i-parse

答案 1 :(得分:21)

简而言之,没有。以这种方式考虑,在JSON中存储符号与在JSON中存储字符串相同。因此,在解析JSON字符串时,您无法区分这两者。您当然可以将字符串键转换回符号,或者实际上甚至构建一个类来与JSON交互,这样可以自动执行此操作,但我建议您只使用字符串。

但是,只是为了它,以前是这个问题的答案,这是有问题的:

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

或许是HashWithIndifferentAccess

答案 2 :(得分:5)

我通过调用with_indifferent_access方法解决了我的类似问题

这里我有一个json字符串,我们可以将它分配给变量s

s = "{\"foo\":{\"bar\":\"cool\"}}"

所以现在我可以使用JSON类解析数据并将其分配给h

h = JSON.parse(s).with_indifferent_access

这将生成一个可以接受字符串或符号作为键的哈希

h[:foo]["bar"]
  #=> "cool"

答案 3 :(得分:3)

  1. 使用ActiveSupport :: JSON.decode,它将允许您更轻松地交换json解析器
  2. 使用ActiveSupport :: JSON.decode(my_json,symbolize_names:true)
  3. 这将递归地符号化哈希中的所有键。

    (在ruby 2.0上确认)

答案 4 :(得分:2)

可以修改哈希中的所有键,将它们从字符串转换为符号:

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

不幸的是,这对嵌套在数组中的哈希不起作用。但是,您可以编写一个转换所有哈希键的递归方法:

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

答案 5 :(得分:0)

您不能像这样使用该选项

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

  

在Rails 4.1或更高版本中,ActiveSupport::JSON.decode不再接受   MultiJSON的选项哈希。 MultiJSON达到了生命的终点   已被删除。

您可以使用symbolize_keys来处理它。

ActiveSupport::JSON.decode(str_json).symbolize_keys