Ich habe eine Datei, in der viele JavaScript-Objekte in JSON-Form gespeichert sind, und ich muss die Datei lesen, jedes der Objekte erstellen und etwas damit tun (in meinem Fall in eine Datenbank einfügen). Die JavaScript-Objekte können in einem Format dargestellt werden:
Format A:
[{name: 'thing1'},
....
{name: 'thing999999999'}]
oder Format B:
{name: 'thing1'} // <== My choice.
...
{name: 'thing999999999'}
Beachten Sie, dass das ...
viele JSON-Objekte anzeigt. Mir ist bewusst, dass ich die gesamte Datei in den Speicher lesen und dann folgendermaßen verwenden JSON.parse()
kann:
fs.readFile(filePath, 'utf-8', function (err, fileContents) {
if (err) throw err;
console.log(JSON.parse(fileContents));
});
Die Datei könnte jedoch sehr groß sein. Ich würde es vorziehen, einen Stream zu verwenden, um dies zu erreichen. Das Problem, das ich bei einem Stream sehe, ist, dass der Dateiinhalt jederzeit in Datenblöcke aufgeteilt werden kann. Wie kann ich ihn also JSON.parse()
für solche Objekte verwenden?
Im Idealfall wird jedes Objekt als separater Datenblock gelesen, aber ich bin mir nicht sicher, wie das geht .
var importStream = fs.createReadStream(filePath, {flags: 'r', encoding: 'utf-8'});
importStream.on('data', function(chunk) {
var pleaseBeAJSObject = JSON.parse(chunk);
// insert pleaseBeAJSObject in a database
});
importStream.on('end', function(item) {
console.log("Woot, imported objects into the database!");
});*/
Hinweis: Ich möchte verhindern, dass die gesamte Datei in den Speicher eingelesen wird. Zeiteffizienz spielt für mich keine Rolle. Ja, ich könnte versuchen, mehrere Objekte gleichzeitig zu lesen und alle gleichzeitig einzufügen, aber das ist eine Leistungsoptimierung - ich brauche einen Weg, der garantiert keine Speicherüberlastung verursacht, unabhängig davon, wie viele Objekte in der Datei enthalten sind .
Ich kann verwenden , wählen FormatA
oder FormatB
oder vielleicht etwas anderes, nur geben Sie bitte Ihre Antwort. Vielen Dank!
quelle
Antworten:
Um eine Datei zeilenweise zu verarbeiten, müssen Sie lediglich das Lesen der Datei und den Code, der auf diese Eingabe einwirkt, entkoppeln. Sie können dies erreichen, indem Sie Ihre Eingabe puffern, bis Sie eine neue Zeile erreichen. Angenommen, wir haben ein JSON-Objekt pro Zeile (im Grunde Format B):
Jedes Mal, wenn der Dateistream Daten vom Dateisystem empfängt, werden diese in einem Puffer gespeichert und dann
pump
aufgerufen.Wenn sich keine neue Zeile im Puffer befindet,
pump
kehren Sie einfach zurück, ohne etwas zu tun. Wenn der Stream das nächste Mal Daten erhält, werden dem Puffer weitere Daten (und möglicherweise eine neue Zeile) hinzugefügt, und dann haben wir ein vollständiges Objekt.Wenn es eine neue Zeile gibt,
pump
wird der Puffer von Anfang bis zur neuen Zeile abgeschnitten und an übergebenprocess
. Es wird dann erneut geprüft, ob sich eine weitere neue Zeile im Puffer befindet (diewhile
Schleife). Auf diese Weise können wir alle Zeilen verarbeiten, die im aktuellen Block gelesen wurden.Schließlich
process
wird einmal pro Eingabezeile aufgerufen. Wenn vorhanden, wird das Wagenrücklaufzeichen entfernt (um Probleme mit den Zeilenenden zu vermeiden - LF vs CRLF) und dannJSON.parse
eine Zeile aufgerufen . An diesem Punkt können Sie mit Ihrem Objekt alles tun, was Sie brauchen.Beachten Sie, dass dies
JSON.parse
streng ist, was als Eingabe akzeptiert wird. Sie müssen Ihre Bezeichner und Zeichenfolgenwerte in doppelte Anführungszeichen setzen . Mit anderen Worten,{name:'thing1'}
wird einen Fehler auslösen; Sie müssen verwenden{"name":"thing1"}
.Da sich immer nur ein Datenblock gleichzeitig im Speicher befindet, ist dies äußerst speichereffizient. Es wird auch extrem schnell sein. Ein schneller Test ergab, dass ich 10.000 Zeilen in weniger als 15 ms verarbeitet habe.
quelle
Gerade als ich dachte, dass es Spaß machen würde, einen Streaming-JSON-Parser zu schreiben, dachte ich auch, dass ich vielleicht eine schnelle Suche durchführen sollte, um zu sehen, ob bereits einer verfügbar ist.
Es stellt sich heraus, dass es gibt.
Da ich es gerade gefunden habe, habe ich es offensichtlich nicht verwendet, daher kann ich seine Qualität nicht kommentieren, aber ich bin gespannt, ob es funktioniert.
Es funktioniert unter Berücksichtigung des folgenden Javascript und
_.isString
:Dadurch werden Objekte beim Eingang protokolliert, wenn der Stream ein Array von Objekten ist. Daher wird jeweils nur ein Objekt gepuffert.
quelle
Ab Oktober 2014 können Sie (mit JSONStream) Folgendes tun: https://www.npmjs.org/package/JSONStream
So demonstrieren Sie anhand eines Arbeitsbeispiels:
data.json:
hallo.js:
quelle
parse('*')
oder Sie werden keine Daten erhalten.var getStream() = function () {
entfernt werden.Mir ist klar, dass Sie nach Möglichkeit vermeiden möchten, die gesamte JSON-Datei in den Speicher einzulesen. Wenn Sie jedoch über den verfügbaren Speicher verfügen, ist dies in Bezug auf die Leistung möglicherweise keine schlechte Idee. Wenn Sie node.js 'require () für eine json-Datei verwenden, werden die Daten sehr schnell in den Speicher geladen.
Ich habe zwei Tests durchgeführt, um festzustellen, wie die Leistung beim Ausdrucken eines Attributs aus jeder Funktion aus einer 81-MB-Geojson-Datei aussieht.
Im ersten Test habe ich die gesamte Geojson-Datei mit in den Speicher eingelesen
var data = require('./geo.json')
. Das dauerte 3330 Millisekunden, und das Ausdrucken eines Attributs aus jedem Feature dauerte 804 Millisekunden, was einer Gesamtsumme von 4134 Millisekunden entspricht. Es schien jedoch, dass node.js 411 MB Speicher verwendete.Im zweiten Test habe ich die Antwort von @ arcseldon mit JSONStream + event-stream verwendet. Ich habe die JSONPath-Abfrage so geändert, dass nur das ausgewählt wird, was ich benötige. Diesmal war der Speicher nie höher als 82 MB, aber das Ganze dauerte jetzt 70 Sekunden!
quelle
Ich hatte ähnliche Anforderungen, ich muss eine große JSON-Datei in Knoten JS lesen und Daten in Chunks verarbeiten und eine API aufrufen und in Mongodb speichern. inputFile.json ist wie folgt:
Jetzt habe ich JsonStream und EventStream verwendet, um dies synchron zu erreichen.
quelle
Ich habe ein Modul namens BFJ geschrieben, das dies kann . Insbesondere kann die Methode
bfj.match
verwendet werden, um einen großen Stream in diskrete Teile von JSON aufzuteilen:Hier
bfj.match
gibt einen lesbaren, Objekt-Modus Strom, den die geparsten Datenelemente erhalten und wird 3 Argumente übergeben:Ein lesbarer Stream, der den Eingabe-JSON enthält.
Ein Prädikat, das angibt, welche Elemente aus dem analysierten JSON in den Ergebnisstrom verschoben werden.
Ein Optionsobjekt, das angibt, dass es sich bei der Eingabe um ein durch Zeilenumbrüche getrenntes JSON handelt (dies dient zum Verarbeiten von Format B aus der Frage, ist für Format A nicht erforderlich).
Beim
bfj.match
Aufrufen wird JSON zuerst aus der Tiefe des Eingabestreams analysiert, wobei das Prädikat mit jedem Wert aufgerufen wird, um zu bestimmen, ob dieses Element in den Ergebnisstrom verschoben werden soll oder nicht. Dem Prädikat werden drei Argumente übergeben:Der Eigenschaftsschlüssel oder Array-Index (dies gilt
undefined
für Elemente der obersten Ebene).Der Wert selbst.
Die Tiefe des Elements in der JSON-Struktur (Null für Elemente der obersten Ebene).
Natürlich kann je nach Bedarf auch ein komplexeres Prädikat verwendet werden. Sie können anstelle einer Prädikatfunktion auch eine Zeichenfolge oder einen regulären Ausdruck übergeben, wenn Sie einfache Übereinstimmungen mit Eigenschaftsschlüsseln durchführen möchten.
quelle
Ich habe dieses Problem mit dem Split-npm-Modul gelöst . Pipe deinen Stream in Split, und es wird " einen Stream aufbrechen und wieder zusammensetzen, so dass jede Zeile ein Stück ist ".
Beispielcode:
quelle
Wenn Sie die Kontrolle über die Eingabedatei haben und es sich um ein Array von Objekten handelt, können Sie dies einfacher lösen. Ordnen Sie die Ausgabe der Datei mit jedem Datensatz in einer Zeile folgendermaßen an:
Dies ist immer noch gültiger JSON.
Verwenden Sie dann das Readline-Modul node.js, um sie zeilenweise zu verarbeiten.
quelle
Ich denke, Sie müssen eine Datenbank verwenden. MongoDB ist in diesem Fall eine gute Wahl, da es JSON-kompatibel ist.
UPDATE : Mit dem Mongoimport- Tool können Sie JSON-Daten in MongoDB importieren.
quelle