Ich möchte mehrere JSON-Objekte nacheinander aus einer Datei / einem Stream in Python lesen. Leider json.load()
nur .read()
bis zum Ende der Datei; Es scheint keine Möglichkeit zu geben, damit ein einzelnes Objekt zu lesen oder träge über die Objekte zu iterieren.
Gibt es eine Möglichkeit, dies zu tun? Die Verwendung der Standardbibliothek wäre ideal, aber wenn es eine Bibliothek eines Drittanbieters gibt, würde ich diese stattdessen verwenden.
Im Moment setze ich jedes Objekt in eine separate Zeile und verwende es json.loads(f.readline())
, aber ich würde es wirklich vorziehen, dies nicht tun zu müssen.
Beispiel Verwendung
example.py
import my_json as json
import sys
for o in json.iterload(sys.stdin):
print("Working on a", type(o))
in.txt
{"foo": ["bar", "baz"]} 1 2 [] 4 5 6
Beispielsitzung
$ python3.2 example.py < in.txt
Working on a dict
Working on a int
Working on a int
Working on a list
Working on a int
Working on a int
Working on a int
python
json
serialization
Jeremy
quelle
quelle
{"foo": ["bar", "baz"]}
in meinem Beispiel), sollteyield
es dies tun und dann mit dem nächsten fortfahren (1
).'\n'
(ein einzelnes Newline, nicht zwei Zeichen) in seiner json Darstellung , da'\n'
muss entwertet wird in einem JSON - String und daher'\n'
kann für die Formatierung nur zB verwendet werden, glaube ichjson.dumps()
doesn‘ t'\n'
standardmäßig einführen . Beachten Sie, dass Unicode-Zeilenumbrüche wie U + 0085 möglicherweise nicht in JSON-Zeichenfolgen angezeigt werden.Antworten:
Hier ist eine viel, viel einfachere Lösung. Das Geheimnis besteht darin, zu versuchen, fehlzuschlagen und die Informationen in der Ausnahme zu verwenden, um sie korrekt zu analysieren. Die einzige Einschränkung ist, dass die Datei durchsuchbar sein muss.
Bearbeiten: habe gerade bemerkt, dass dies nur für Python> = 3.5 funktioniert. Bei früheren Fehlern geben Fehler einen ValueError zurück, und Sie müssen die Position aus der Zeichenfolge analysieren, z
quelle
re
wird nicht funktionieren - die Backslashes müssen entkommen. Betrachten Sie eine rohe Zeichenfolger'...'
.ujson
anstelle von verwenden, werdenjson
Sie eine enorme Beschleunigung bekommenJSON ist im Allgemeinen nicht sehr gut für diese Art der inkrementellen Verwendung. Es gibt keine Standardmethode zum Serialisieren mehrerer Objekte, sodass sie problemlos einzeln geladen werden können, ohne das gesamte Los zu analysieren.
Die von Ihnen verwendete Objekt-pro-Zeile-Lösung wird auch an anderer Stelle angezeigt. Scrapy nennt es "JSON-Zeilen":
Sie können es etwas mehr Pythonisch tun:
Ich denke, dies ist der beste Weg - es basiert nicht auf Bibliotheken von Drittanbietern und es ist leicht zu verstehen, was los ist. Ich habe es auch in meinem eigenen Code verwendet.
quelle
Ein bisschen spät vielleicht, aber ich hatte genau dieses Problem (mehr oder weniger). Meine Standardlösung für diese Probleme besteht normalerweise darin, nur einen Regex-Split für ein bekanntes Stammobjekt durchzuführen, aber in meinem Fall war dies unmöglich. Die einzige Möglichkeit, dies generisch zu tun, besteht darin, einen geeigneten Tokenizer zu implementieren .
Nachdem ich keine ausreichend generische und einigermaßen leistungsfähige Lösung gefunden hatte, beendete ich dies selbst und schrieb das
splitstream
Modul. Es ist ein Pre-Tokenizer, der JSON und XML versteht und einen kontinuierlichen Stream zum Parsen in mehrere Blöcke aufteilt (die eigentliche Analyse bleibt jedoch Ihnen überlassen). Um eine gewisse Leistung zu erzielen, wird es als C-Modul geschrieben.Beispiel:
quelle
Sicher können Sie das tun. Sie müssen nur
raw_decode
direkt nehmen. Diese Implementierung lädt die gesamte Datei in den Speicher und verarbeitet diese Zeichenfolge (ähnlich wiejson.load
). Wenn Sie große Dateien haben, können Sie diese so ändern, dass sie nur nach Bedarf ohne große Schwierigkeiten aus der Datei gelesen werden können.Verwendung: Genau wie Sie es gewünscht haben, ist es ein Generator.
quelle
Dies ist ein ziemlich unangenehmes Problem, da Sie in Zeilen streamen müssen, aber Musterübereinstimmungen über mehrere Zeilen hinweg mit geschweiften Klammern, aber auch Musterübereinstimmungen json. Es ist eine Art JSON-Vorbereitung, gefolgt von einer JSON-Analyse. Json ist im Vergleich zu anderen Formaten einfach zu analysieren, so dass es nicht immer notwendig ist, eine Analysebibliothek zu verwenden. Wie sollten wir diese widersprüchlichen Probleme lösen?
Generatoren zur Rettung!
Das Schöne an Generatoren für ein Problem wie dieses ist, dass Sie sie übereinander stapeln können, um die Schwierigkeit des Problems schrittweise zu beseitigen und gleichzeitig die Faulheit zu bewahren. Ich habe auch überlegt, den Mechanismus zum Zurückgeben von Werten an einen Generator (send ()) zu verwenden, aber zum Glück musste ich das nicht verwenden.
Um das erste der Probleme zu lösen, benötigen Sie eine Art Streamingfinditer als Streaming-Version von re.finditer. Mein Versuch, dies unten zu tun, zieht nach Bedarf Zeilen ein (kommentieren Sie die Debug-Anweisung aus, um sie zu sehen), während Sie immer noch Übereinstimmungen zurückgeben. Ich habe es dann tatsächlich leicht modifiziert, um nicht übereinstimmende Linien sowie Übereinstimmungen zu erhalten (im ersten Teil des erhaltenen Tupels als 0 oder 1 markiert).
Damit ist es dann möglich, bis zu geschweiften Klammern abzugleichen, jedes Mal zu berücksichtigen, ob die Klammern ausgeglichen sind, und dann entweder einfache oder zusammengesetzte Objekte zurückzugeben.
Dies gibt Tupel wie folgt zurück:
Im Grunde ist das der böse Teil. Wir müssen jetzt nur noch die letzte Stufe des Parsens durchführen, wie wir es für richtig halten. Zum Beispiel können wir die Iterload-Funktion von Jeremy Roman (Danke!) Verwenden, um eine einzelne Zeile zu analysieren:
Probier es aus:
Ich erhalte diese Ergebnisse (und wenn Sie diese Debug-Zeile aktivieren, werden Sie sehen, dass sie bei Bedarf in den Zeilen eingezogen wird):
Dies funktioniert nicht in allen Situationen. Aufgrund der Implementierung der
json
Bibliothek ist es unmöglich , vollständig korrekt zu arbeiten, ohne den Parser selbst neu zu implementieren.quelle
"}"
und in diesen"]"
vorkommen? Ich denke, dies ist eine allgemeine Einschränkung beim Parsen mit Regex.Ich glaube, ein besserer Weg wäre die Verwendung einer Zustandsmaschine. Unten finden Sie einen Beispielcode, den ich durch Konvertieren eines NodeJS-Codes unter dem folgenden Link in Python
3ausgearbeitet habe(verwendetes nicht lokales Schlüsselwort nur in Python 3 verfügbar, Code funktioniert in Python 2 nicht).Edit-1: Code aktualisiert und mit Python 2 kompatibel gemacht
Edit-2: Eine reine Python3-Version wurde ebenfalls aktualisiert und hinzugefügt
https://gist.github.com/creationix/5992451
Nur Python 3-Version
Python 2-kompatible Version
Testen Sie es
Die Ausgabe davon ist
quelle
Ich möchte eine Lösung anbieten. Der Schlüsselgedanke ist, zu versuchen, zu dekodieren: Wenn dies fehlschlägt, geben Sie ihm mehr Vorschub, andernfalls verwenden Sie die Versatzinformationen, um die nächste Dekodierung vorzubereiten.
Das aktuelle JSON-Modul toleriert jedoch nicht, dass SPACE im Kopf des zu decodierenden Strings verwendet wird, sodass ich sie entfernen muss.
========================= Ich habe mehrere txt-Dateien getestet und es funktioniert einwandfrei. (in1.txt)
(in2.txt)
(in.txt, Ihre Initiale)
(Ausgabe für Benedikts Testfall)
quelle
Hier ist meins:
quelle
Ich habe die elegante Lösung von @ wuilang verwendet. Der einfache Ansatz - ein Byte lesen, versuchen zu dekodieren, ein Byte lesen, versuchen zu dekodieren, ... - funktionierte, war aber leider sehr langsam.
In meinem Fall habe ich versucht, "hübsch gedruckte" JSON-Objekte desselben Objekttyps aus einer Datei zu lesen. Dadurch konnte ich den Ansatz optimieren; Ich konnte die Datei Zeile für Zeile lesen und nur dekodieren, wenn ich eine Zeile fand, die genau "}" enthielt:
Wenn Sie zufällig mit einem kompakten JSON mit einer Zeile arbeiten, der Zeilenumbrüchen in Zeichenfolgenliteralen entgeht, können Sie diesen Ansatz noch sicherer vereinfachen:
Offensichtlich funktionieren diese einfachen Ansätze nur für ganz bestimmte Arten von JSON. Wenn diese Annahmen jedoch zutreffen, funktionieren diese Lösungen korrekt und schnell.
quelle
Wenn Sie eine json.JSONDecoder-Instanz verwenden, können Sie die
raw_decode
Member-Funktion verwenden. Es gibt ein Tupel der Python-Darstellung des JSON-Werts und einen Index zurück, an dem die Analyse gestoppt wurde. Dies macht es einfach, die verbleibenden JSON-Werte zu schneiden (oder in einem Stream-Objekt zu suchen). Ich bin nicht so glücklich über die zusätzliche while-Schleife, um den Leerraum zwischen den verschiedenen JSON-Werten in der Eingabe zu überspringen, aber meiner Meinung nach wird die Aufgabe erledigt.Die nächste Version ist viel kürzer und frisst den Teil der Zeichenfolge, der bereits analysiert wurde. Es scheint, dass aus irgendeinem Grund ein zweiter Aufruf von json.JSONDecoder.raw_decode () fehlschlägt, wenn das erste Zeichen in der Zeichenfolge ein Leerzeichen ist. Dies ist auch der Grund, warum ich das Leerzeichen im Whileloop oben überspringe ...
In der Dokumentation zur Klasse json.JSONDecoder enthält die Methode raw_decode https://docs.python.org/3/library/json.html#encoders-and-decoders Folgendes:
Und diese fremden Daten können leicht ein weiterer JSON-Wert sein. Mit anderen Worten, die Methode könnte zu diesem Zweck geschrieben werden.
Mit der input.txt unter Verwendung der oberen Funktion erhalte ich die Beispielausgabe wie in der ursprünglichen Frage dargestellt.
quelle
Sie können https://pypi.org/project/json-stream-parser/ für genau diesen Zweck verwenden.
Ausgabe
quelle