2010-01-13 21 views
22

Entiendo que en JSON, se supone que las teclas están rodeadas por comillas dobles. Sin embargo, estoy usando una fuente de datos que no los cita, lo que hace que el analizador Ruby JSON genere un error. ¿Hay alguna manera de realizar un análisis sintáctico "no estricto"?Análisis JSON sin claves entrecomilladas

Ejemplo:

>> JSON.parse('{name:"hello", age:"23"}') 
JSON::ParserError: 618: unexpected token at '{name:"hello", age:"23"}' 
    from /Library/Ruby/Gems/1.8/gems/json-1.1.7/lib/json/common.rb:122:in `parse' 
    from /Library/Ruby/Gems/1.8/gems/json-1.1.7/lib/json/common.rb:122:in `parse' 
    from (irb):5 
>> JSON.parse('{"name":"hello", "age":"23"}') 
=> {"name"=>"hello", "age"=>"23"} 
>> 

(He intentado utilizar una expresión regular para agregar las cotizaciones antes de analizar, pero no pudo conseguirlo completamente de trabajo).

+9

Si no es JSON, no es JSON. Puede que se parezca superficialmente a él, pero la solución correcta es arreglar la fuente, por lo que en realidad le da a JSON algo de algo que se parece a algo como JSON pero no lo es. –

+0

Lamentablemente no tengo control sobre la fuente, es de un tercero. –

+0

http://www.google.com/ig/calculator?hl=es&q=100AUD=?USD por ejemplo requiere lo que está pidiendo. @floyd tiene la solución a continuación, y es aceptable – Rabbott

Respuesta

15

Si los datos están muy bien formado aparte de eso, una expresión regular simple podría hacer que:

irb(main):009:0> '{name:"hello", age:"23"}'.gsub(/([a-z]+):/, '"\1":') 
=> "{\"name\":\"hello\", \"age\":\"23\"}" 
+5

''{nombre:" hello ", edad:" 23 "} '.gsub (/ ([\ w] +): /,' "\ 1": ') 'lo hace un poco más robusto! – ankimal

+3

Se produce un error miserable si el valor es una marca de tiempo. Por ejemplo, {name: "Hello", time: "12:59:59"} – Prabhakar

6

Curiosamente, su ejemplo es la sintaxis ruby ​​1.9 Hash válida. Si sus datos son tan simples como esto (sin espacios ni otros caracteres especiales en los nombres de las teclas), y puede procesarlos en un contexto seguro, puede simplemente eval.

irb(main):001:0> eval '{name:"hello", age:"23"}' 
=> {:name=>"hello", :age=>"23"} 

Esto le da símbolos como llaves, por lo que después del proceso si es necesario convertirlos en cadenas:

irb(main):002:0> eval('{name:"hello", age:"23"}').reduce({}) {|h,(k,v)| h[k.to_s] = v; h} 
=> {"name"=>"hello", "age"=>"23"} 
+1

Gracias por eso, aunque desde que estoy usando 1.8.7 esa no es una opción en este momento. –

+0

¡Solución muy limpia! Gracias. Mucho más limpio para aprovechar esta información de Google en lugar de buscar otra gema. – ylluminate

+5

Esto puede ser muy peligroso ... (por ejemplo, si obtiene del servidor '{a: 1}; \' rm -rf/\ '') – ghayes

1

(respondiendo a mi propia pregunta) El fragmento que Floyd publicado fue similar a lo que he intentado - que estaba fallando debido a que algunos de mis cadenas contienen dos puntos. Pero persistí y encontré una solución:

gsub(/([\{|\,}])\s*([a-zA-Z]+):/, '\1 "\2":') 
+0

El problema es que su expresión regular también reemplazará las instancias de "key =" dentro de un valor entrecomillado, que uno no quiere. –

2
gsub(/(\w+)\s*:/, '"\1":') 

funcionado mejor que

gsub(/([a-z]+):/, '"\1":') 

Si tuviera espacios o letras mayúsculas, que fracasó.

8

Tengo el mismo problema con un feed de datos de un tercero, pero el mío devuelve una respuesta JSON más complicada que las soluciones gsub no manejan. Después de algunas investigaciones, parece que estos datos son en realidad literales de objetos de JavaScript que no requieren que se indiquen las claves.

Para resolver el problema, agregué la gema execjs e instalé node.js (la gema de therubyracer también funcionaría probablemente). Una vez completado, lo siguiente devuelve un hash de ruby ​​analizado correctamente.

ExecJS.eval('{name:"hello", age:"23"}') 
=> {"name"=>"hello", "age"=>"23"} 
0

Esta es la forma en que he tenido que resolverlo:

JSON.parse(broken_json_string.gsub(/'([^']+)':/, '"\1":')) 

Algunos de lo anterior supone las claves sólo contienen letras; algunos de los nuestros contenían guiones bajos, espacios, etc. Es más fácil decir "cualquier personaje que no sea una sola cita" (dado que, en nuestro caso, todas las teclas estaban encerradas entre comillas simples).