web-dev-qa-db-fra.com

Ruby L'analyse JSON modifie les clés de hachage

Disons que j'ai ce Hash:

{
  :info => [
    {
        :from => "Ryan Bates",
        :message => "sup bra",
        :time => "04:35 AM"
    }
  ]
}

Je peux appeler le tableau d'informations en faisant hash[:info].

Maintenant, lorsque je transforme cela en JSON (JSON.generate), puis l'analyser (JSON.parse), j'obtiens ce hachage:

{
  "info" => [
    {
        "from" => "Ryan Bates",
        "message" => "sup bra",
        "time" => "04:35 AM"
    }
  ]
}

Maintenant, si j'utilise hash[:info] il renvoie nil, mais pas si j'utilise hash["info"].

Pourquoi est-ce? Et existe-t-il de toute façon pour corriger cette incompatibilité (outre l'utilisation des clés de chaîne dès le départ)?

63

Bref, non. Pensez-y de cette façon, le stockage de symboles dans JSON est le même que le stockage de chaînes dans JSON. Il est donc impossible de distinguer les deux lorsqu'il s'agit d'analyser la chaîne JSON. Vous pouvez bien sûr reconvertir les clés de chaîne en symboles, ou même créer une classe pour interagir avec JSON qui le fait automatiquement, mais je recommanderais simplement d'utiliser des chaînes.

Mais, juste pour le plaisir, voici les réponses à cette question les fois précédentes où elle a été posée:

quelle est la meilleure façon de convertir une paire de valeurs de clés au format json en Ruby hachage avec symbole comme clé?

ActiveSupport :: JSON décode les symboles de perte de hachage

Ou peut-être un HashWithIndifferentAccess

21
Lee Jarvis

Le générateur JSON convertit les symboles en chaînes car JSON ne prend pas en charge les symboles. Étant donné que les clés JSON sont toutes des chaînes, l'analyse d'un document JSON produira un hachage Ruby avec des clés de chaîne par défaut.

Vous pouvez dire à l'analyseur d'utiliser des symboles au lieu de chaînes en utilisant le symbolize_names option.

Exemple:

original_hash = {:info => [{:from => "Ryan Bates", :message => "sup bra", :time => "04:35 AM"}]}
serialized = JSON.generate(original_hash)
new_hash = JSON.parse(serialized, {:symbolize_names => true})

new_hash[:info]
 #=> [{:from=>"Ryan Bates", :message=>"sup bra", :time=>"04:35 AM"}]

Référence: http://www.Ruby-doc.org/stdlib-1.9.3/libdoc/json/rdoc/JSON.html#method-i-parse

134
wyattisimo

J'ai résolu mon problème similaire en appelant la méthode with_indifferent_access dessus

Ici, j'ai une chaîne json et nous pouvons l'assigner à la variable s

s = "{\"foo\":{\"bar\":\"cool\"}}"

Alors maintenant, je peux analyser les données avec la classe JSON et les affecter à h

h = JSON.parse(s).with_indifferent_access

Cela produira un hachage qui peut accepter une chaîne ou un symbole comme clé

h[:foo]["bar"]
  #=> "cool"
5
aaron.v
  1. Utilisez ActiveSupport :: JSON.decode, cela vous permettra d'échanger plus facilement les analyseurs json
  2. Utilisez ActiveSupport :: JSON.decode (my_json, symbolize_names: true)

Cela symbolisera récursivement toutes les clés du hachage.

(confirmé le Ruby 2.0)

3
Jdfreefly

Il est possible de modifier toutes les clés d'un hachage pour les convertir d'une chaîne en symbole:

symbol_hash = Hash[obj.map{ |k,v| [k.to_sym, v] }]

puts symbol_hash[:info]
# => {"from"=>"Ryan Bates", "message"=>"sup bra", "time"=>"04:35 AM"}

Malheureusement, cela ne fonctionne pas pour le hachage imbriqué dans le tableau. Vous pouvez cependant écrire une petite méthode récursive qui convertit toutes les clés de hachage:

def symbolize_keys(obj)
  #puts obj.class # Useful for debugging
  return obj.collect { |a| symbolize_keys(a) } if obj.is_a?(Array)
  return obj unless obj.is_a?(Hash)
  return Hash[obj.map{ |k,v| [k.to_sym, symbolize_keys(v)] }]
end

symbol_hash = symbolize_keys(hash)
puts symbol_hash[:info]
# => {:from=>"Ryan Bates", :message=>"sup bra", :time=>"04:35 AM"}
2
Kathryn

Vous ne pouvez pas utiliser cette option comme celle-ci

ActiveSupport::JSON.decode(str_json, symbolize_names: true)

Dans Rails 4.1 ou version ultérieure, ActiveSupport::JSON.decode n'accepte plus un hachage d'options pour MultiJSON. MultiJSON a atteint sa fin de vie et a été supprimé.

Vous pouvez utiliser symbolize_keys pour le gérer.

ActiveSupport::JSON.decode(str_json).symbolize_keys
0
rails_id