Ruby JSON 解析更改哈希键

Posted

技术标签:

【中文标题】Ruby JSON 解析更改哈希键【英文标题】:Ruby JSON parse changes Hash keys 【发布时间】:2011-11-24 12:28:01 【问题描述】:

假设我有这个哈希:


  :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"],则不会。

这是为什么?有没有办法解决这个不兼容问题(除了从一开始就使用字符串键)?

【问题讨论】:

【参考方案1】:

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 (rubydoc.info/gems/multi_json/1.3.2/MultiJson) @Boushley:不,仍然是symbolize_names。我的回答引用了 Ruby 标准库附带的 JSON 引擎。您正在引用第三方 gem。【参考方案2】:

简而言之,不。这样想,在 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

【讨论】:

感谢您的链接,但就像您说的,我将使用字符串作为键【参考方案3】:

我通过调用 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 你是对的。这是一个链接api.rubyonrails.org/v4.2.5/classes/ActiveSupport/…【参考方案4】:
    使用 ActiveSupport::JSON.decode,它可以让您更轻松地交换 json 解析器 使用 ActiveSupport::JSON.decode(my_json, symbolize_names: true)

这将递归地表示散列中的所有键。

(在 ruby​​ 2.0 上确认)

【讨论】:

似乎在 rails 4.1 中被移除了? edgeguides.rubyonrails.org/… 和 api.rubyonrails.org/classes/ActiveSupport/JSON.html 状态选项支持已删除【参考方案5】:

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

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"

【讨论】:

【参考方案6】:

你不能像这样使用那个选项

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

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

您可以使用symbolize_keys 来处理它。 警告:它仅适用于解析为哈希的 JSON 字符串。

ActiveSupport::JSON.decode(str_json).symbolize_keys

【讨论】:

以上是关于Ruby JSON 解析更改哈希键的主要内容,如果未能解决你的问题,请参考以下文章

在 Ruby 中更新哈希键的惯用方法是啥?

Ruby更改哈希的键值

Ruby 程序不保存哈希更改

在更改 ruby​​ 中的某些值后,如何维护具有原始值的哈希副本?

在Ruby中将嵌套哈希键从CamelCase转换为snake_case

如何将 JSON 转换为 Ruby 哈希