Ruby JSON解析更改哈希键

73

假设我有这个哈希(Hash):

{
  :info => [
    {
        :from => "Ryan Bates",
        :message => "sup bra",
        :time => "04:35 AM"
    }
  ]
}
我可以通过hash[:info]来调用信息数组。
现在,当我将其转换为JSON(JSON.generate),然后解析它(JSON.parse),我得到以下哈希:
{
  "info" => [
    {
        "from" => "Ryan Bates",
        "message" => "sup bra",
        "time" => "04:35 AM"
    }
  ]
}

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

为什么会这样?是否有任何方法可以解决这个不兼容性(除了一开始就使用字符串键)?

6个回答

160

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


我认为现在应该是:symbolize_keys了 (http://rubydoc.info/gems/multi_json/1.3.2/MultiJson) - Boushley
13
不,仍然是 symbolize_names。我的回答涉及到了 Ruby 标准库中自带的 JSON 引擎。而你引用了一个第三方 gem。 - wyattisimo

25
简而言之,不行。想一想,将符号存储在JSON中与将字符串存储在JSON中相同。因此,在解析JSON字符串时,您不可能区分两者之间的区别。当然,您可以将字符串键转换回符号,或者实际上甚至构建一个与JSON交互的类,自动执行此操作,但我建议仅使用字符串。
但是,仅为了这个问题,以下是以前提出该问题时的答案: 最佳方法将JSON格式的键值对转换为具有符号作为键的Ruby哈希表? ActiveSupport :: JSON解码哈希表失去符号 或者也许是HashWithIndifferentAccess

谢谢提供链接,但就像你说的,我将仅使用字符串作为键。 - LanguagesNamedAfterCofee

7

我用with_indifferent_access方法解决了类似的问题。

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

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

现在我可以使用JSON类解析数据并将其赋值给h变量。

h = JSON.parse(s).with_indifferent_access

这将生成一个哈希,可以接受字符串或符号作为键。
h[:foo]["bar"]
  #=> "cool"

with_indifferent_access 是通过 Rails 生态系统中的某些东西添加到 Hash 中的吗? - Mark
1
@Mark,你是正确的。这里是一个链接:http://api.rubyonrails.org/v4.2.5/classes/ActiveSupport/HashWithIndifferentAccess.html - aaron.v

3
  1. 使用ActiveSupport::JSON.decode,这将使您更轻松地交换json解析器。
  2. 使用ActiveSupport::JSON.decode(my_json, symbolize_names: true)

这将递归地将哈希中的所有键转换为符号。

(在ruby 2.0上确认过)


似乎在Rails 4.1中被移除了?http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#changes-in-json-handling和http://api.rubyonrails.org/classes/ActiveSupport/JSON.html指出选项支持已被移除。 - nruth

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

1
您不能像这样使用该选项。
Rails 4.1或更高版本中,ActiveSupport::JSON.decode不再接受MultiJSON的选项哈希。MultiJSON已经到达了生命周期的尽头并被移除。
您可以使用symbolize_keys来处理它。 警告:它仅适用于解析为哈希的JSON字符串。
ActiveSupport::JSON.decode(str_json).symbolize_keys

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接