json形式のキーと値のペアを記号をキーとするルビーハッシュに変換する最良の方法は何ですか?


102

json形式のキーと値のペアを、記号をキーとするルビハッシュに変換する最良の方法は何でしょうか。例:

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

これを行うことができるヘルパーメソッドはありますか?


これhttp://stackoverflow.com/a/43773159/1297435をRails 4.1で試してください
rails_id

回答:


255

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

4
ちなみに、Ruby 1.9にはこのライブラリが含まれています。
Simon Perepelitsa 2012

これはかつてあり:symbolize_keysませんでしたか?なぜその名前が変わったのですか?
Lukas

5
@Lukas:symbolize_keysRailsのことです。
wyattisimo 2013年


19

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

方法3:Marshal.load-ネストされたキーの元の文字列/シンボルミックスを保持します。パーフェクト!

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

私が気づいていない欠点がない限り、私は方法3が進むべき道だと思います。

乾杯


2
ここであなたが反対側を制御できるという保証はありません。そのため、JSON形式に固執する必要があると思います。両側を完全に制御できる場合、Marshalは確かに優れたフォーマットですが、汎用のシリアル化には適していません。
chills42 2014年

5

トリックを実行するために組み込まれているものはありませんが、JSON gemを使用してそれを実行するためのコードを記述することはそれほど難しくありません。symbolize_keysRailsを使用している場合は、それに組み込まれているメソッドがありますが、これは必要なように再帰的にキーをシンボル化するものではありません。

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

1

もちろん、json gemがありますが、これは二重引用符のみを処理します。


madlepが以下で言うように、JSONが有効であることがわかっている場合は、これで十分です(たとえば、自分で作成する場合など)
edavey

これは機能しません。JSON.parse(JSON.generate([:a])) # => ["a"]
ジャスティンL.

2
それは、JSONがシンボルを表現できないためです。Marshal.load(Marshal.dump([:a]))代わりに:を使用できます。
Leventix

1

これを処理するもう1つの方法は、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年のこのセキュリティホールはまだ修正されていませんか?
bert bruynooghe

1
Ruby 2.2以降、シンボルはGCされています。YAML.load任意のオブジェクト(たとえば、キャッシュ)をシリアル化するためのものです。提案されているYAML.safe_loadことが正しいことを利用しての問題ですので、数ヶ月そのブログ記事の後に導入されました:github.com/ruby/psych/commit/...
レイフ

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]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.