2016-07-10 15 views
6

Ich verwende Rails 4.2.3. Ich analysiere JSON, das von einer dritten Partei gesendet wurde (ich habe keine Kontrolle darüber, wie diese JSON entsteht). Ich bemerkte taht sie sehr selten senden schlecht JSON, wie soIst es möglich, schlechte Anführungszeichen in einer schlecht geformten JSON-Zeichenfolge zu erkennen und dann die Zeichenfolge als JSON ordnungsgemäß zu analysieren?

'{"DisplayName":""fat" Tony Elvis ","Time":null,"OverallRank":19,"AgeRank":4}' 

Beachten Sie in der oben das Wort „Fett“, mit den Anführungszeichen, Schrauben der Rest des JSON auf. In meinem Rails-Code, analysieren ich das JSON, wie so ...

json_data = JSON.parse(content_str) 

Obwohl kann ich Fehler fangen, wenn JSON nicht richtig analysieren, ich frage mich, ob es einen Weg gibt für diese schlecht platziert Anführungszeichen Konto korrigiere sie, so dass die obige Zeichenfolge kein fehlerhaftes JSON darstellt, und analysiere dann das JSON ordnungsgemäß.

+0

Natürlich können Sie versuchen, den JSON zu korrigieren, aber was würden Sie mit '{" a ":", "b": 1 "}' tun. Sie sollten kein ungültiges JSON akzeptieren, da es sich um einen unsicheren und unvorhersehbaren Wert handelt und Sie einfach entkommen können. z.B. '\" fat \ "wäre gültig. – Julian

+0

Ich möchte den fehlerhaften JSON (in der oben beschriebenen Weise) wenn möglich korrigieren. Wenn das nicht möglich ist, dann ist das auch eine Antwort. –

+0

Sie müssten im Grunde Ihren eigenen Json Fixer machen. Vielleicht teilen Sie die Zeichenfolge, wo Sie finden "," und andere Variationen mit Leerzeichen/neue Zeilen dazwischen, dann nehmen Sie alles nach jedem ':', nehmen Sie die erste und letzte doppelte Anführungszeichen und ersetzen Sie schließlich jedes doppelte Anführungszeichen durch eine einzige , beispielsweise. Dann könnten Sie es im Parser verwenden. Ein anderer Weg wäre, den JSON-Anbieter zu kontaktieren und sie bitten, einen gültigen JSON zur Verfügung zu stellen. – George

Antwort

2

Wenn Sie genau wissen, was Fehlbildungen auftreten können, können Sie verwalten ein paar verrückte Abhilfen zu tun regex wie mit der Zeichenfolge übereinstimmen und zu korrigieren, bevor es als json Parsen:

(?:")([^,:"]*"[^,:"]*"[^,:"]*)(?:") 

http://regexr.com/3dpj1

Aber diese ist definitiv etwas, das Sie nicht tun sollten, wenn nicht absolut notwendig !! Sie versuchen besser, den Quellenbesitzer zu kontaktieren und ihn dazu zu bringen, den Zitaten korrekt zu entkommen!

bearbeiten: Hier ist ein Full-POC, wo unescaped Anführungszeichen einfach entfernt werden: https://jsfiddle.net/MattDiMu/y8khwfw6/

+0

Danke. Mein knoweld von regulärem Ausdruck und Rails ist immer noch etwas begrenzt. Ich sehe, dass dieser Ausdruck die schlechten Zitate identifiziert, aber wie benutze ich das, um die Situation zu beheben? –

+0

@Mike - Ich habe eine vollständige POC im ursprünglichen Post hinzugefügt. – MattDiMu

+0

Gutes Geschäft. Wenn ich Rails dazu nutze (im Gegensatz zu JS), wie würde das in RoR funktionieren? –

0

Versuchen Fehlerbehandlung Rettungsausnahmebehandlung mit beginnen wie,

begin 
json_data = JSON.parse(content_str) 
rescue =>e 
Rails.logger.debug e 
end 

Diese Ausnahme auslösen, wenn es JSON ist ungültig Formatieren und informieren Sie den Quellenbesitzer, um den JSON zu ändern.

+1

Ich verstehe nicht, wie diese Antwort mir helfen wird, die Arten von Zitatfehlbildungen zu erkennen, die ich beschrieben habe, und sie weiter zu beheben. –

1

Mit Regex können Sie überprüfen, bevor für doppelte Anführungszeichen \"\" gefolgt von einem Wort \w+ und endend mit \". Wenn Sie es finden, verwenden Sie gsub, um die Phrase durch einfache Anführungszeichen und einen Lookback "\'\\1\' zu ersetzen.

t='{"DisplayName":""fat" Tony Elvis ","Time":null,"OverallRank":19,"AgeRank":4}' 
t=t.gsub(/\"\"(\w+)\"/, '"\'\\1\'') 
1

Ich glaube, Sie zu machen haben/haben einige Annahmen über die „json“, die immer wahr sind. Wenn zum Beispiel die JSON-Objekte immer eine feste Reihenfolge von Attributen haben, kann das sehr hilfreich sein, besonders wenn nur einzelne Attribute problematisch sind.

Ich würde versuchen,

{"DisplayName":"(.*?)","Time":(null|"[^"]*"),"OverallRank":(\d+),"AgeRank":(\d+)} 

passen und dann mit Hilfe von einigen „fixer“ Funktion zu ersetzen, die wahrscheinlich nur die Capture-Gruppen verwendet und neu codiert eine Ad-hoc-Objekt erstellt zurück in tatsächlich gültig Json. Eine Variante wäre, die (.*?) so zu erweitern, dass sie nur dann zusammenpasst, wenn etwas nicht stimmt.

Der ganze Ansatz wird jedoch komplizierter mit optionalen Attributen und noch mehr mit einer flexiblen Reihenfolge von Attributen (die alle immer noch überschaubar sind).

Wie Sie sehr wohl bemerkt haben, funktioniert das nur, wenn die Annahme an der Spitze wahr ist. Abhängig von den Annahmen, die Sie treffen können, kann die Lösung sehr einfach sein. Es wird jedoch alles unhandlich, wenn diese missgebildeten Elemente völlig unregelmäßig sind. Damit ...Viel Glück, schätze ich. Bitte posten Sie die Annahmen über den JSON, den Sie für wahr halten, wenn Sie weitere Hilfe benötigen. Wenn es aber keine gibt, müsste ein Programm erraten, was eigentlich gemeint ist. Ich meine, jemand könnten bedeuten:

{ 
"DisplayName":"I want to have a quotationmark followed by Time, all quoted and 
       separated by a comma \",\"Time\":null, because that's how I roll 
       and this entry shall not have a Time attribute...", 
"OverallRank":2, 
"AgeRank":2 
} 

wenn die Anführungszeichen in einer falschen Weise entkommen lassen, werden Sie ein Problem bekommen. Aber wie gesagt, Sie müssen einige Annahme über die "JSON" machen. Ich meine, die übliche Annahme über Json ist, dass es gültig ist, weil es sonst einfach nicht Json ist.

0

Dies ist kein einfaches Problem zu lösen. Hauptsächlich, weil das Schreiben eines JSON-Parsers nicht-trivial ist, und ich bezweifle, dass Sie in der Lage wären, einen Parser so anzupassen, dass er so funktioniert, wie Sie möchten.

Wenn ich dieses Problem unbedingt programmgesteuert lösen musste (um den Hersteller zu bitten, ihren JSON zu reparieren), würde ich es wahrscheinlich mit Verzweigung tun.

Unter Ihrem Beispiel JSON-String:
{"DisplayName":""fat" Tony Elvis ","Time":null,"OverallRank":19,"AgeRank":4}

zuerst die Eingabe in Zeichen aufzubrechen und über sie iterieren. Jedes Mal, wenn ein Angebot angetroffen wird, werden beide Möglichkeiten rekursiviert und getestet: Das Angebot ist Teil des JSON und das Angebot ist Teil der Daten.

Jedes Mal, wenn Sie ein Angebot finden Sie verzweigen, so dass nach zwei Zitaten wird es vier mögliche gültige Lösungen sein, nach vier Anführungszeichen es 16 sein werden mögliche Lösungen usw.

Wie Sie dies tun, streame jede mögliche Lösung in einen Stream-JSON-Parser (like this one) und beobachte Ausnahmen. Wenn man geworfen wird, nehme an, die mögliche Lösung funktioniert nicht und wirf sie weg. Ich würde auch nach einer Tiefe von 4 (oder 8, wenn Sie doppelt zitierte Strings in Ihren Daten erwarten) wegwerfen. Durch die Begrenzung der Tiefe werden auch Lösungen wie {"a\":\"b\", \"c"} davon abgehalten, als gültig zurückgegeben zu werden.

Eigentlich bauen diese zumindest einige Stunden dauern würde, wahrscheinlich ein paar Tage richtig zu machen, und es gibt immer noch eine gute Möglichkeit, es falsch-positive Ergebnisse berichten werde. Es wird auch langsam als Hund sein, weil Sie möglicherweise Tausende von verschiedenen JSON-Streams mit Ruby analysieren müssen, anstatt einen mit einer C-JSON-Bibliothek zu analysieren.

Sie könnten einige der Performance-Probleme lindern, indem sie alle möglichen Lösungen in eine Warteschlange anhängen und einen Pool von Arbeitsthreads verwenden, um mögliche Lösungen und die Arbeit an ihnen zu holen; Aber jetzt reden wir vielleicht über eine Woche Arbeit, um diese Daten mit einem Skript zu bereinigen.

0

Soweit schlechte Angebote betrifft, sollte diese regex_pattern in der Lage sein, es durch \" zu ersetzen. Hier ist ein Beispiel Rails-Snippet:

regex_pattern = /(?<=[^\[{:,\\]|")"(?=[^:,\}\]])/ 
corrected_content_str = content_str.gsub(regex_pattern, '\\"') 

Dieses Muster folgende Regeln hat:

  • Ein doppeltes Anführungszeichen soll nicht die folgenden Symbole, bevor sie haben: Öffnen eckige Klammer, geschweifte Klammer, Dickdarm-, Komma, Schrägstrich und ein Doppelzitat. Daher (?<=[^\[{:,\\]|").
  • Ein doppeltes Anführungszeichen soll nicht die folgenden Symbole, nachdem sie hat: Doppelpunkt, Komma, geschweifte Klammer zu schließen, und eckige Klammer zu schließen. Daher (?=[^:,\}\]]).

http://rubular.com/r/YBfcJYCf6D

Dieses behebt nicht ungepaarten zitiert, though.

0

Wie die anderen Poster erwähnt haben, gibt es keine Möglichkeit, sicherzustellen, dass Sie die Daten, die sie Ihnen senden, nicht lesen können, wenn Ihr Dienst Ihnen kein gültiges JSON zur Verfügung stellt. Was Sie jedoch tun können, ist, einige häufige Fälle zu finden und diese zu korrigieren.

Wenn Ihre JSON-Dokumente dem Schema in Ihrem Beispiel folgen, hilft Ihnen das Schreiben eines kleinen Parsers beim Lesen fehlerhafter Dokumente, die sich daran halten.


Flucht Doppel Zitate -Diese werden wird tick Ihre unescaped doppelte Anführungszeichen zurück, auch wenn sie nicht ausgeglichen sind.

invalid = '{"DisplayName":""fat" Tony" Elvis","Time":null,"OverallRank":19,"AgeRank":4}' 

# strip away { and } 
tailhead = invalid[1..-2] 

props = tailhead.split(/,(?=".+"\s*:)/) 

pairs = props.map {|p| p.split(/:(?=(?:".*"|\d+|null|false|true)$)/i)} 

escaped = pairs.map do |k,v| 
    # is this a string property? 
    string = v[/^"(.*?)"$/, 1] 
    string ? [k, "\"#{string.gsub(/"/,'\\"')}\""] : [k,v] 
end 

valid = '{' + escaped.map {|p| p.join(':')}.join(',') + '}' 

json_data = JSON.parse(valid) 

Jedes Mal, wenn Sie einen Ausschnitt wie die oben werfen eine Ausnahme haben, stellen Sie sicher, dass alle Angaben Datensatz im Protokoll. Wenn Sie weitere Beispielfälle sammeln, können Sie Ihre Handhabung verbessern.

Ich bin kein Rubyist, aber ich bin mir ziemlich sicher, dass Sie etwas mit einem Start-Rescue-Block tun könnten, wo Sie nur den obigen Code mit dem Ruby-JSON-Parser aufrufen müssten.