Json形式のキー値のペアをRubyハッシュをキーとするハッシュに変換する最良の方法は何ですか?
{ 'user': { 'name': 'foo', 'age': 40, 'location': { 'city' : 'bar', 'state': 'ca' } } }
==>
{ :user=>{ :name => 'foo', :age =>'40', :location=>{ :city => 'bar', :state=>'ca' } } }
これを行うことができるヘルパーメソッドはありますか?
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"}
Leventix、ご回答ありがとうございます。
Marshal.load(Marshal.dump(h))メソッドは、元のキータイプを再帰的に保持するため、さまざまなメソッドの中で最も整合性が高いと思われます。
これは、文字列と記号キーが混在するネストされたハッシュがあり、デコード時にそのミックスを保持する場合に重要です(たとえば、非常に複雑な/ネストされた3番目に加えて、独自のカスタムオブジェクトがハッシュに含まれている場合に発生する可能性があります) -プロジェクト時間の制約など、何らかの理由でキーを操作/変換できないパーティオブジェクト。
例えば。:
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"] } }
方法:Marshal.load-ネストされたキーの元の文字列/シンボルミックスを保持します。完璧!
Marshal.load( Marshal.dump(h) )
=> { :youtube => { :search => "daffy", "history" => ["goofy", "mickey"] } }
気付いていない欠点がない限り、方法3が道だと思います。
乾杯
このトリックを実行するために組み込まれたものは何もありませんが、JSON gemを使用してそれを実行するためのコードを記述することはそれほど難しくありません。 symbolize_keys
メソッドは、Railsを使用している場合は組み込まれていますが、必要なようにキーを再帰的にシンボル化しません。
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は二重引用符でフォーマットする必要があります)。このコードは、解析する前にそれをクリーンアップします。
再帰的方法:
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
これを処理する別の方法は、YAMLシリアル化/逆シリアル化を使用することです。これにより、キーの形式も保持されます。
YAML.load({test: {'test' => { ':test' => 5}}}.to_yaml)
=> {:test=>{"test"=>{":test"=>5}}}
このアプローチの利点は、REST services ...
もちろん、 json gem がありますが、それは二重引用符のみを処理します。
最も便利な方法は、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]