我想知道将符号作为键将json格式的键值对转换为ruby hash的最佳方法是什么: 例如:
{ 'user': { 'name': 'foo', 'age': 40, 'location': { 'city' : 'bar', 'state': 'ca' } } }
==>
{ :user=>{ :name => 'foo', :age =>'40', :location=>{ :city => 'bar', :state=>'ca' } } }
是否有辅助方法可以做到这一点?
答案 0 :(得分:240)
在解析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"}
答案 1 :(得分:19)
Leventix,谢谢你的回答。
Marshal.load(Marshal.dump(h))方法可能具有各种方法的最完整性,因为它会保留原始密钥类型递归。
这很重要,如果你有一个混合了字符串和符号键的嵌套哈希,并且你想在解码时保留这种混合(例如,如果你的哈希包含你自己的自定义对象以及高度复杂的话,这可能会发生/嵌套的第三方对象,其密钥由于某种原因无法操纵/转换,如项目时间约束)。
E.g:
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 - 在嵌套键中保留原始字符串/符号混合。 PERFECT!
Marshal.load( Marshal.dump(h) )
=> { :youtube => { :search => "daffy", "history" => ["goofy", "mickey"] } }
除非有一个我不知道的缺点,否则我认为方法3是可行的方法。
干杯
答案 2 :(得分:5)
没有内置任何功能,但使用JSON gem编写代码并不难。如果您正在使用Rails,则会在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应该用双引号格式化)。在尝试解析之前,这段代码将清除它。
答案 3 :(得分: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
答案 4 :(得分:1)
当然,有一个json gem,但只处理双引号。
答案 5 :(得分:1)
另一种处理方法是使用YAML序列化/反序列化,它也保留了密钥的格式:
YAML.load({test: {'test' => { ':test' => 5}}}.to_yaml)
=> {:test=>{"test"=>{":test"=>5}}}
这种方法的好处似乎是一种更适合REST服务的格式......
答案 6 :(得分:0)
最方便的方法是使用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]
答案 7 :(得分:0)
如果您认为您可能同时需要字符串和符号键:
JSON.parse(json_string).with_indifferent_access