Tengo una cadena que parece un hash:
"{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }"
¿Cómo obtengo un Hash? me gusta:
{ :key_a => { :key_1a => 'value_1a', :key_2a => 'value_2a' }, :key_b => { :key_1b => 'value_1b' } }
La cadena puede tener cualquier profundidad de anidamiento. Tiene todas las propiedades de cómo se escribe un Hash válido en Ruby.
Respuestas:
La cadena creada al llamar
Hash#inspect
se puede volver a convertir en un hash llamandoeval
. Sin embargo, esto requiere que lo mismo sea cierto para todos los objetos en el hash.Si empiezo con el hash
{:a => Object.new}
, entonces su representación de cadena es"{:a=>#<Object:0x7f66b65cf4d0>}"
, y no puedo usareval
para volver a convertirlo en un hash porque#<Object:0x7f66b65cf4d0>
no es válida la sintaxis de Ruby.Sin embargo, si todo lo que está en el hash son cadenas, símbolos, números y matrices, debería funcionar, porque tienen representaciones de cadenas que son válidas para la sintaxis de Ruby.
fuente
eval
evaluará como un hash asegurándome de que la declaración anterior sea válida para esa cadena.eval
en el lugar equivocado es un gran agujero de seguridad. Cualquier cosa dentro de la cadena, será evaluada. Así que imagina si en una API alguien inyectararm -fr
Para diferentes cadenas, puede hacerlo sin usar un
eval
método peligroso :fuente
JSON.parse(hash_as_string.gsub("=>", ":").gsub(":nil,", ":null,"))
El método rápido y sucio sería
Pero tiene graves implicaciones de seguridad.
Ejecuta lo que se pasa, debe estar 110% seguro (como en, al menos, ninguna entrada del usuario en ningún lugar del camino) contendría solo hashes formados correctamente o errores inesperados / criaturas horribles del espacio exterior podrían comenzar a aparecer.
fuente
Quizás YAML.load?
fuente
Este pequeño fragmento lo hará, pero no puedo verlo trabajando con un hash anidado. Aunque creo que es bastante lindo
Pasos 1. Elimino el '{', '}' y el ':' 2. Me divido en la cadena donde encuentre un ',' 3. Divido cada una de las subcadenas que se crearon con la división, cada vez que encuentra a '=>'. Luego, creo un hash con los dos lados del hash que acabo de separar. 4. Me queda una serie de hashes que luego combino.
EJEMPLO DE ENTRADA: "{: user_id => 11,: blog_id => 2,: comment_id => 1}" RESULTADO DE SALIDA: {"user_id" => "11", "blog_id" => "2", "comment_id" = > "1"}
fuente
{}:
caracteres de los valores dentro del hash stringified?Las soluciones hasta ahora cubren algunos casos pero fallan algunos (ver más abajo). Aquí está mi intento de una conversión más completa (segura). Sé de un caso de esquina que esta solución no maneja, que son símbolos de un solo carácter compuestos de caracteres extraños pero permitidos. Por ejemplo
{:> => :<}
es un hash de rubí válido.También puse este código en github . Este código comienza con una cadena de prueba para ejercitar todas las conversiones.
Aquí hay algunas notas sobre las otras soluciones aquí.
eval
es demasiado aterrador para mí (como señala correctamente Toms).fuente
:nil
a:null
manejar ese particular, la rareza.Yo tuve el mismo problema. Estaba almacenando un hash en Redis. Al recuperar ese hash, era una cadena. No quería llamar
eval(str)
por motivos de seguridad. Mi solución fue guardar el hash como una cadena json en lugar de una cadena ruby hash. Si tiene la opción, usar json es más fácil.TL; DR: uso
to_json
yJSON.parse
fuente
to_json
yJSON.parse
Prefiero abusar de ActiveSupport :: JSON. Su enfoque es convertir el hash a yaml y luego cargarlo. Desafortunadamente, la conversión a yaml no es simple y probablemente quieras pedirla prestada a AS si aún no tienes AS en tu proyecto.
También tenemos que convertir cualquier símbolo en teclas de cadena normales ya que los símbolos no son apropiados en JSON.
Sin embargo, es incapaz de manejar los hashes que tienen una cadena de fecha (nuestras cadenas de fecha terminan no rodeadas de cadenas, que es donde entra el gran problema):
string = '{' last_request_at ': 2011-12-28 23:00:00 UTC}'
ActiveSupport::JSON.decode(string.gsub(/:([a-zA-z])/,'\\1').gsub('=>', ' : '))
Resultaría en un error de cadena JSON no válido cuando intenta analizar el valor de la fecha.
Me encantaría cualquier sugerencia sobre cómo manejar este caso
fuente
ActiveSupport::JSON.decode(response.body, symbolize_keys: true)
funciona en rails 4.1 y admite símbolos sin comillas {: a => 'b'}
simplemente agregue esto a la carpeta de inicializadores:
fuente
Creé una gema hash_parser que primero comprueba si un hash es seguro o no usa
ruby_parser
gema. Solo entonces, aplica eleval
.Puedes usarlo como
Las pruebas en https://github.com/bibstha/ruby_hash_parser/blob/master/test/test_hash_parser.rb le dan más ejemplos de las cosas que he probado para garantizar que eval sea seguro.
fuente
Por favor considere esta solución. Biblioteca + especificaciones:
Archivo
lib/ext/hash/from_string.rb
:Archivo
spec/lib/ext/hash/from_string_spec.rb
:fuente
it "generally works"
pero no necesariamente? Sería más detallado en esas pruebas.it "converts strings to object" { expect('...').to eql ... }
it "supports nested objects" { expect('...').to eql ... }
Llegué a esta pregunta después de escribir una línea para este propósito, así que comparto mi código en caso de que ayude a alguien. Funciona para una cadena con solo un nivel de profundidad y posibles valores vacíos (pero no nulos), como:
El codigo es:
fuente
Se encontró con un problema similar que necesitaba usar eval ().
Mi situación, estaba extrayendo algunos datos de una API y escribiéndolos en un archivo localmente. Luego poder extraer los datos del archivo y usar Hash.
Usé IO.read () para leer el contenido del archivo en una variable. En este caso, IO.read () lo crea como una cadena.
Luego usé eval () para convertir la cadena en un hash.
También solo para mencionar que IO es un antepasado de File. Por lo tanto, también puede usar File.read en su lugar si lo desea.
fuente