Je me demande quel est le meilleur moyen de convertir une paire valeur-clé formatée JSON en un hachage Ruby avec le symbole en tant que clé: Exemple:
{ 'user': { 'name': 'foo', 'age': 40, 'location': { 'city' : 'bar', 'state': 'ca' } } }
==>
{ :user=>{ :name => 'foo', :age =>'40', :location=>{ :city => 'bar', :state=>'ca' } } }
Y at-il une méthode d'assistance peut faire cela?
en utilisant la gemme json lors de l'analyse de la chaîne json, vous pouvez passer l'option symbolize_names. Voir ici: http://flori.github.com/json/doc/index.html (regardez sous parse)
par exemple:
>> s ="{\"akey\":\"one\",\"bkey\":\"two\"}"
>> JSON.parse(s,:symbolize_names => true)
=> {:akey=>"one", :bkey=>"two"}
Leventix, merci pour votre réponse.
La méthode Marshal.load (Marshal.dump (h)) possède probablement la plus grande intégrité des différentes méthodes car elle conserve les types de clé d'origine récursivement.
Cela est important dans le cas où vous avez un hachage imbriqué avec une combinaison de clés de chaîne et de symbole et que vous souhaitez conserver ce mélange lors du décodage (par exemple, cela pourrait se produire si votre hachage contient vos propres objets personnalisés en plus de tiers hautement complexes -party objets dont les clés ne peuvent pas être manipulées/converties pour une raison quelconque, comme une contrainte de temps de projet).
Par exemple.:
h = {
:youtube => {
:search => 'daffy', # nested symbol key
'history' => ['goofy', 'mickey'] # nested string key
}
}
Méthode 1 : JSON.parse - symbolise toutes les clés de manière récursive => Ne conserve pas le mélange d'origine
JSON.parse( h.to_json, {:symbolize_names => true} )
=> { :youtube => { :search=> "daffy", :history => ["goofy", "mickey"] } }
Méthode 2 : ActiveSupport :: JSON.decode - symbolise uniquement les clés de niveau supérieur => Ne conserve pas le mélange d'origine
ActiveSupport::JSON.decode( ActiveSupport::JSON.encode(h) ).symbolize_keys
=> { :youtube => { "search" => "daffy", "history" => ["goofy", "mickey"] } }
Méthode 3 : Marshal.load - conserve le mélange de chaînes/symboles d'origine dans les clés imbriquées. PARFAIT!
Marshal.load( Marshal.dump(h) )
=> { :youtube => { :search => "daffy", "history" => ["goofy", "mickey"] } }
À moins d'un inconvénient que je ne sache pas, je penserais que la méthode 3 est la voie à suivre.
À votre santé
Il n'y a rien d'intégré pour faire le tour, mais il n'est pas trop difficile d'écrire le code pour le faire en utilisant la gemme JSON. Si vous l'utilisez, il existe une méthode symbolize_keys
intégrée à Rails, mais elle ne symbolise pas les clés de manière récursive comme vous le souhaitez.
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
Comme Leventix l'a dit, la gemme JSON ne gère que les chaînes entre guillemets (ce qui est techniquement correct - JSON doit être formaté avec des guillemets). Ce morceau de code va nettoyer cela avant d'essayer de l'analyser.
Méthode récursive:
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
Bien sûr, il existe un json gem , mais cela ne gère que les guillemets doubles.
Une autre façon de gérer cela consiste à utiliser la sérialisation/désérialisation YAML, qui préserve également le format de la clé:
YAML.load({test: {'test' => { ':test' => 5}}}.to_yaml)
=> {:test=>{"test"=>{":test"=>5}}}
L'avantage de cette approche semble être un format mieux adapté aux services REST ...
Le moyen le plus pratique consiste à utiliser le joyau Nice_hash: 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]