2010-01-13 19 views
22

Rozumiem, że w JSON klucze powinny być otoczone podwójnymi cudzysłowami. Jednak używam źródła danych, które ich nie cytuje, co powoduje, że parser Ruby JSON wywołuje błąd. Czy istnieje sposób wykonywania "nie ścisłego" analizowania?Parsowanie JSON bez cytowanych kluczy

Przykład:

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

(próbowałem za pomocą wyrażenia regularnego dodać cytaty przed parsowania, ale nie mógł on w pełni funkcjonalny).

+9

Jeśli to nie JSON, to nie JSON. To może na pozór przypominać, ale właściwym rozwiązaniem jest naprawienie źródła, tak więc faktycznie daje ono JSON'owi coś nieistniejącego - to wygląda trochę jak JSON-ale-nie jest. –

+0

Niestety nie mam kontroli nad źródłem, to od strony trzeciej. –

+0

http://www.google.com/ig/calculator?hl=pl&q=100AUD=?USD na przykład wymaga tego, o co prosi. @floyd ma rozwiązanie poniżej - i jest akceptowalne – Rabbott

Odpowiedz

15

Jeśli dane są dość dobrze uformowane, inny niż ten, proste regex może zrobić to:

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

''{imię i nazwisko:" cześć ", wiek:" 23 "} '.gsub (/ ([\ w] +): /,' "\ 1": ') 'sprawia, że ​​jest trochę bardziej wytrzymały! – ankimal

+3

Przerywa marnie, jeśli wartość jest znacznikiem czasu. Na przykład {nazwa: "Cześć", czas: "12:59:59"} – Prabhakar

6

Co ciekawe, Twój przykład jest poprawny. Jeśli twoje dane są naprawdę tak proste (bez spacji i innych znaków specjalnych w nazwach kluczy) i możesz przetworzyć je w bezpiecznym kontekście, możesz po prostu eval go.

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

Daje to symbole jak klucze, więc post-proces, jeśli trzeba przekształcić je w ciągi:

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

Dziękuję za to, chociaż od tego czasu używam wersji 1.8.7, która nie jest obecnie dostępna. –

+0

Bardzo czyste rozwiązanie! Dzięki. Znacznie czystsze, aby wykorzystać te informacje z Google, zamiast szukać innego klejnotu. – ylluminate

+5

Może to być bardzo niebezpieczne ... (np. Jeśli otrzymasz z serwera '{a: 1}; \' rm -rf/\ '') – ghayes

1

(Odpowiadając na moje własne pytanie) fragment że Floyd pisał był podobny do tego, co próbowałem - została niepowodzeniem, ponieważ niektóre z moich strun zawierać średników. Ale trwało i znalazł rozwiązanie:

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

Problem polega na tym, że twoje wyrażenie regularne będzie również zastępować wystąpienia "klucz =" w cytowanej wartości, której nie chcesz. –

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

pracował lepiej niż

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

Jeśli miał spacje lub wielkie litery, nie udało się.

8

Mam ten sam problem z plikiem danych innej firmy, ale mój zwraca bardziej skomplikowaną odpowiedź podobną do JSON, której rozwiązania gsub nie obsługują. Po pewnym badaniu wydaje się, że te pliki danych są w rzeczywistości literałami obiektów JavaScript, które nie wymagają cytowania kluczy.

Aby rozwiązać problem, dodałem klej execjs i zainstalowane node.js (klej termodruk prawdopodobnie też by działał). Po zakończeniu zwraca następujący poprawnie przeanalizowany skrót mieszania ruby.

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

ten sposób miałem go rozwiązać:

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

Niektóre z powyższym zakłada klucze zawierać tylko litery; niektóre z nich zawierały podkreślenia, spacje itp. Łatwiej powiedzieć "każda postać, która nie jest pojedynczym cudzysłowem" (w naszym przypadku wszystkie klucze były zawijane w pojedyncze cudzysłowy).