将 json 格式的键值对转换为以符号为键的 ruby​​ 哈希的最佳方法是啥?

Posted

技术标签:

【中文标题】将 json 格式的键值对转换为以符号为键的 ruby​​ 哈希的最佳方法是啥?【英文标题】:what is the best way to convert a json formatted key value pair to ruby hash with symbol as key?将 json 格式的键值对转换为以符号为键的 ruby​​ 哈希的最佳方法是什么? 【发布时间】:2010-12-16 11:06:53 【问题描述】:

我想知道将 json 格式的键值对转换为以符号为键的 ruby​​ 哈希的最佳方法是什么: 示例:

 'user':  'name': 'foo', 'age': 40, 'location':  'city' : 'bar', 'state': 'ca'   
==> 
 :user=> :name => 'foo', :age =>'40', :location=> :city => 'bar', :state=>'ca'   

有没有辅助方法可以做到这一点?

【问题讨论】:

试试这个http://***.com/a/43773159/1297435 for rails 4.1 【参考方案1】:

在解析 json 字符串时使用 json gem 可以传入 symbolize_names 选项。见这里:http://flori.github.com/json/doc/index.html(在解析下查看)

例如:

>> s ="\"akey\":\"one\",\"bkey\":\"two\""
>> JSON.parse(s,:symbolize_names => true)
=> :akey=>"one", :bkey=>"two" 

【讨论】:

顺便说一句,Ruby 1.9 包含这个库。 这不是以前的:symbolize_keys 吗?为什么这个名字变了? @Lukas: symbolize_keys 是 Rails 的东西。 :symbolize_names 虽然是 Ruby 的东西【参考方案2】:

Leventix,感谢您的回答。

Marshal.load(Marshal.dump(h)) 方法可能是各种方法中最完整的,因为它递归地保留了原始密钥类型。 p>

如果您有一个包含字符串和符号键混合的嵌套哈希,并且您希望在解码时保留该混合(例如,如果您的哈希包含您自己的自定义对象以及高度复杂的对象,则可能会发生这种情况/嵌套的第三方对象,无论出于何种原因,您都无法对其键进行操作/转换,例如项目时间限制)。

例如:

h = 
      :youtube => 
                    :search   => 'daffy',                 # nested symbol key
                    'history' => ['goofy', 'mickey']      # nested string key
                  
    

方法 1:JSON.parse - 递归地表示所有键 => 不保留原始组合

JSON.parse( h.to_json, :symbolize_names => true )
  =>  :youtube =>  :search=> "daffy", :history => ["goofy", "mickey"]   

方法 2:ActiveSupport::JSON.decode - 仅象征***键 => 不保留原始组合

ActiveSupport::JSON.decode( ActiveSupport::JSON.encode(h) ).symbolize_keys
  =>  :youtube =>  "search" => "daffy", "history" => ["goofy", "mickey"]  

方法 3:Marshal.load - 在嵌套键中保留原始字符串/符号组合。完美!

Marshal.load( Marshal.dump(h) )
  =>  :youtube =>  :search => "daffy", "history" => ["goofy", "mickey"]  

除非有我不知道的缺点,否则我认为方法 3 是可行的方法。

干杯

【讨论】:

这里不能保证你可以控制对方,所以我相信你必须坚持JSON格式。如果双方都完全控制,那么 Marshal 确实是一个不错的格式,但是不适合通用的序列化。【参考方案3】:

没有任何内置的东西可以做到这一点,但是使用 JSON gem 编写代码来做到这一点并不难。如果您正在使用 Rails 中内置了一个 symbolize_keys 方法,但它不会像您需要的那样递归地表示键。

require 'json'

def json_to_sym_hash(json)
  json.gsub!('\'', '"')
  parsed = JSON.parse(json)
  symbolize_keys(parsed)
end

def symbolize_keys(hash)
  hash.inject()|new_hash, key_value|
    key, value = key_value
    value = symbolize_keys(value) if value.is_a?(Hash)
    new_hash[key.to_sym] = value
    new_hash
  
end

正如 Leventix 所说,JSON gem 只处理双引号字符串(这在技术上是正确的 - JSON 应该用双引号格式化)。这段代码会在尝试解析之前清理它。

【讨论】:

【参考方案4】:

递归方法:

require 'json'

def JSON.parse(source, opts = )
  r = JSON.parser.new(source, opts).parse
  r = keys_to_symbol(r) if opts[:symbolize_names]
  return r
end

def keys_to_symbol(h)
  new_hash = 
  h.each do |k,v|
    if v.class == String || v.class == Fixnum || v.class == Float
      new_hash[k.to_sym] = v
    elsif v.class == Hash
      new_hash[k.to_sym] = keys_to_symbol(v)
    elsif v.class == Array
      new_hash[k.to_sym] = keys_to_symbol_array(v)
    else
      raise ArgumentError, "Type not supported: #v.class"
    end
  end
  return new_hash
end

def keys_to_symbol_array(array)
  new_array = []
  array.each do |i|
    if i.class == Hash
      new_array << keys_to_symbol(i)
    elsif i.class == Array
      new_array << keys_to_symbol_array(i)
    else
      new_array << i
    end
  end
  return new_array
end

【讨论】:

【参考方案5】:

当然,有一个json gem,但它只处理双引号。

【讨论】:

正如 madlep 在下面所说的那样 - 如果您知道 JSON 将是有效的(例如,您自己制作!),这就是您所需要的全部内容! 这不起作用。 JSON.parse(JSON.generate([:a])) # =&gt; ["a"] 那是因为 JSON 不能表示符号。您可以改用:Marshal.load(Marshal.dump([:a]))【参考方案6】:

另一种处理方法是使用 YAML 序列化/反序列化,这也保留了密钥的格式:

YAML.load(test: 'test' =>  ':test' => 5.to_yaml) 
=> :test=>"test"=>":test"=>5

这种方法的好处似乎是一种更适合 REST 服务的格式...

【讨论】:

永远不要让用户输入进入 YAML.load:tenderlovemaking.com/2013/02/06/yaml-f7u12.html @Rafe 你的意思是 2013 年的这个安全漏洞今天还没有修复吗? 符号从 Ruby 2.2 开始被 GC 处理。 YAML.load 用于序列化任意对象(例如用于缓存)。提议的YAML.safe_load 是在那篇博文发布几个月后推出的,所以要使用正确的东西:github.com/ruby/psych/commit/…【参考方案7】:

最方便的方法是使用 nice_hash gem:https://github.com/MarioRuiz/nice_hash

require 'nice_hash'
my_str = " 'user':  'name': 'foo', 'age': 40, 'location':  'city' : 'bar', 'state': 'ca'   "

# on my_hash will have the json as a hash
my_hash = my_str.json

# or you can filter and get what you want
vals = my_str.json(:age, :city)

# even you can access the keys like this:
puts my_hash._user._location._city
puts my_hash.user.location.city
puts my_hash[:user][:location][:city]

【讨论】:

【参考方案8】:

如果你认为你可能需要字符串和符号键:

JSON.parse(json_string).with_indifferent_access

【讨论】:

以上是关于将 json 格式的键值对转换为以符号为键的 ruby​​ 哈希的最佳方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

将字符串数组转换为键值对

R 和 MongoDB:数组存储为以索引为键的对象

pig 新手,如何使用 pig 中的键值对子集将 JSON 转换为另一个 JSON?

将NSString转换为键值对

将 1 行表展平为键值对表

如何通过 jq 中这些键的键和值对 json 文件进行排序