2011-09-25 41 views
50

Digamos que tengo esta Hash:Rubí JSON de análisis cambia las claves hash

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

puedo llamar a la matriz de información haciendo hash[:info].

Ahora cuando enciendo esto en JSON (JSON.generate), y luego analizarlo (JSON.parse), consigo este hash:

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

Ahora si uso hash[:info] vuelve nil, pero no si uso hash["info"].

¿Por qué es esto? ¿Y hay alguna forma de solucionar esta incompatibilidad (además de usar claves de cadena desde el principio)?

Respuesta

19

En resumen, no. Piénselo de esta manera, almacenar símbolos en JSON es lo mismo que almacenar cadenas en JSON. Por lo tanto, no es posible distinguir entre los dos cuando se trata de analizar la cadena JSON. Por supuesto, puede convertir las claves de cadena de nuevo en símbolos, o de hecho, incluso crear una clase para interactuar con JSON, lo que hace esto automágicamente, pero yo recomendaría simplemente usar cadenas.

Pero, por el simple hecho de que, aquí están las respuestas a esta pregunta las veces anteriores que ha sido preguntado:

what is the best way to convert a json formatted key value pair to ruby hash with symbol as key?

ActiveSupport::JSON decode hash losing symbols

O tal vez un HashWithIndifferentAccess

+0

Gracias por los enlaces, pero como usted ha dicho, sólo voy a utilizar cadenas para las teclas – LanguagesNamedAfterCofee

118

El El generador JSON convierte símbolos en cadenas porque JSON no admite símbolos. Como las claves JSON son todas cadenas, el análisis sintáctico de un documento JSON producirá un hash Ruby con claves de cadena de forma predeterminada.

Puede decirle al analizador que use símbolos en lugar de cadenas utilizando la opción symbolize_names.

Ejemplo:

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

Referencia: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/json/rdoc/JSON.html#method-i-parse

+0

Gracias por la información :) Y – LanguagesNamedAfterCofee

+0

Creo que es: symbolize_keys now (http://rubydoc.info/gems/multi_json/1.3.2/MultiJson) – Boushley

+9

@Boushley: No, sigue siendo 'symbolize_names'. Mi respuesta hace referencia al motor JSON que se envía con la biblioteca estándar de Ruby. Estás haciendo referencia a una gema de terceros. – wyattisimo

2
  1. Uso ActiveSupport :: JSON.decode, se permitirá cambiar analizadores JSON más fácil
  2. Uso ActiveSupport :: JSON.decode (my_json, symbolize_names: true)

Esto simbolizará recursivamente todas las claves en el hash.

(confirmado en ruby ​​2.0)

+0

parece ser eliminado en los carriles 4.1? http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html#changes-in-json-handling y http://api.rubyonrails.org/classes/ActiveSupport/JSON.html indica que se eliminó la opción de soporte – nruth

4

he resuelto mi problema similar con una llamada al método with_indifferent_access en él

Aquí tengo una cadena JSON y podemos asignarle a la variable s

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

Así que ahora puedo analizar los datos con la clase JSON y asignarla a h

h = JSON.parse(s).with_indifferent_access 

Esto producirá un hash que puede aceptar una cadena o un símbolo como la clave

h[:foo]["bar"] 
    #=> "cool" 
+0

Se agregó 'with_indifferent_access' a 'Hash' a través de algo en el ecosistema de Rails? – Mark

+1

@Mark tienes razón. Aquí hay un enlace http://api.rubyonrails.org/v4.2.5/classes/ActiveSupport/HashWithIndifferentAccess.html –

0

No puede utilizar esa opción como esta

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

En Rails 4.1 o posterior, ActiveSupport::JSON.decode ya no acepta un hash de opciones para MultiJSON. MultiJSON llegó al final de su vida y se eliminó .

Puedes usar symbolize_keys para manejarlo.

ActiveSupport::JSON.decode(str_json).symbolize_keys 
2

Es posible modificar todas las claves en un hash para convertirlos de una cadena a un símbolo:

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

desgracia que no funciona para el resumen anidada dentro de la matriz. Puede, sin embargo, escribir un poco método recursivo que convierte todas las claves hash:

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