recorrer mostrar leer externo ejemplos ejemplo datos crear con javascript json file node.js

mostrar - string to json javascript



Analizar un gran archivo JSON en Nodejs (9)

Tengo un archivo que almacena muchos objetos de JavaScript en formato JSON y necesito leer el archivo, crear cada uno de los objetos y hacer algo con ellos (insertarlos en un db en mi caso). Los objetos JavaScript pueden representarse en un formato:

Formato A:

[{name: ''thing1''}, .... {name: ''thing999999999''}]

o Formato B:

{name: ''thing1''} // <== My choice. ... {name: ''thing999999999''}

Tenga en cuenta que ... indica una gran cantidad de objetos JSON. Soy consciente de que podría leer el archivo completo en la memoria y luego usar JSON.parse() esta manera:

fs.readFile(filePath, ''utf-8'', function (err, fileContents) { if (err) throw err; console.log(JSON.parse(fileContents)); });

Sin embargo, el archivo podría ser muy grande, preferiría usar una secuencia para lograr esto. El problema que veo con una transmisión es que el contenido del archivo se puede dividir en fragmentos de datos en cualquier momento, entonces, ¿cómo puedo usar JSON.parse() en dichos objetos?

Idealmente, cada objeto se leería como un fragmento de datos por separado, pero no estoy seguro de cómo hacerlo .

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!"); });*/

Tenga en cuenta que deseo evitar leer el archivo completo en la memoria. La eficiencia del tiempo no me importa. Sí, podría intentar leer una serie de objetos a la vez e insertarlos todos a la vez, pero eso es un ajuste de rendimiento: necesito una forma que garantice que no se produzca una sobrecarga de memoria, sin importar cuántos objetos contenga el archivo .

Puedo elegir usar FormatA o FormatB o tal vez otra cosa, solo por favor especifique en su respuesta. ¡Gracias!


Creo que necesitas usar una base de datos. MongoDB es una buena opción en este caso porque es compatible con JSON.

ACTUALIZACIÓN : puede usar la herramienta mongoimport para importar datos JSON en MongoDB.

mongoimport --collection collection --file collection.json


Escribí un módulo que puede hacer esto, llamado BFJ . Específicamente, el método bfj.match se puede utilizar para dividir una secuencia grande en fragmentos discretos de JSON:

const bfj = require(''bfj''); const fs = require(''fs''); const stream = fs.createReadStream(filePath); bfj.match(stream, (key, value, depth) => depth === 0, { ndjson: true }) .on(''data'', object => { // do whatever you need to do with object }) .on(''dataError'', error => { // a syntax error was found in the JSON }) .on(''error'', error => { // some kind of operational error occurred }) .on(''end'', error => { // finished processing the stream });

Aquí, bfj.match devuelve una bfj.match legible en modo de objeto que recibirá los elementos de datos analizados y se pasan 3 argumentos:

  1. Una secuencia legible que contiene la entrada JSON.

  2. Un predicado que indica qué elementos del JSON analizado se enviarán a la secuencia de resultados.

  3. Un objeto de opciones que indica que la entrada es JSON delimitada por nueva línea (esto es para procesar el formato B de la pregunta, no es necesario para el formato A).

Al ser llamado, bfj.match analizará JSON desde la profundidad de la corriente de entrada primero, llamando al predicado con cada valor para determinar si desea o no empujar ese elemento a la secuencia de resultados. El predicado se pasa tres argumentos:

  1. La clave de propiedad o el índice de matriz (esto no estará undefined para los elementos de nivel superior).

  2. El valor en sí

  3. La profundidad del elemento en la estructura JSON (cero para elementos de nivel superior).

Por supuesto, también se puede usar un predicado más complejo según sea necesario de acuerdo con los requisitos. También puede pasar una cadena o una expresión regular en lugar de una función de predicado, si desea realizar coincidencias simples con las claves de propiedad.


Justo cuando estaba pensando que sería divertido escribir un analizador JSON de transmisión, también pensé que tal vez debería hacer una búsqueda rápida para ver si ya había uno disponible.

Resulta que hay.

Como acabo de encontrarlo, obviamente no lo he usado, así que no puedo comentar sobre su calidad, pero me interesará saber si funciona.

Sí funciona considere el siguiente CoffeeScript:

stream.pipe(JSONStream.parse(''*'')) .on ''data'', (d) -> console.log typeof d console.log "isString: #{_.isString d}"

Esto registrará los objetos tal como vienen si la transmisión es una matriz de objetos. Por lo tanto, lo único que se amortigua es un objeto a la vez.


Me doy cuenta de que, si es posible, es mejor evitar leer todo el archivo JSON en la memoria; sin embargo, si tiene memoria disponible, puede que no sea una mala idea para el rendimiento. El uso de node.js require () en un archivo json carga los datos en la memoria muy rápido.

Ejecuté dos pruebas para ver cómo se veía el rendimiento al imprimir un atributo de cada característica de un archivo geojson de 81MB.

En la primera prueba, leo todo el archivo geojson en la memoria usando var data = require(''./geo.json'') . Eso tomó 3330 milisegundos y luego imprimir un atributo de cada característica tomó 804 milisegundos para un gran total de 4134 milisegundos. Sin embargo, parecía que node.js estaba usando 411MB de memoria.

En la segunda prueba, utilicé la respuesta de @arcseldon con JSONStream + event-stream. Modifiqué la consulta JSONPath para seleccionar solo lo que necesitaba. Esta vez, la memoria nunca fue más alta que 82 MB, sin embargo, ¡todo el proceso tardó 70 segundos en completarse!


Para procesar un archivo línea por línea, simplemente tiene que desacoplar la lectura del archivo y el código que actúa sobre esa entrada. Puede lograr esto almacenando en búfer su entrada hasta que llegue a una nueva línea. Suponiendo que tenemos un objeto JSON por línea (básicamente, formato B):

var stream = fs.createReadStream(filePath, {flags: ''r'', encoding: ''utf-8''}); var buf = ''''; stream.on(''data'', function(d) { buf += d.toString(); // when data is read, stash it in a string buffer pump(); // then process the buffer }); function pump() { var pos; while ((pos = buf.indexOf(''/n'')) >= 0) { // keep going while there''s a newline somewhere in the buffer if (pos == 0) { // if there''s more than one newline in a row, the buffer will now start with a newline buf = buf.slice(1); // discard it continue; // so that the next iteration will start with data } processLine(buf.slice(0,pos)); // hand off the line buf = buf.slice(pos+1); // and slice the processed data off the buffer } } function processLine(line) { // here''s where we do something with a line if (line[line.length-1] == ''/r'') line=line.substr(0,line.length-1); // discard CR (0x0D) if (line.length > 0) { // ignore empty lines var obj = JSON.parse(line); // parse the JSON console.log(obj); // do something with the data here! } }

Cada vez que la secuencia de archivos recibe datos del sistema de archivos, se guarda en un búfer y luego se llama a la pump .

Si no hay línea nueva en el búfer, la pump simplemente vuelve sin hacer nada. Se agregarán más datos (y posiblemente una nueva línea) al búfer la próxima vez que la transmisión obtenga datos, y luego tendremos un objeto completo.

Si hay una línea nueva, la pump corta el búfer desde el principio hasta la nueva línea y lo deja para process . Luego verifica nuevamente si hay otra nueva línea en el búfer (el bucle while). De esta manera, podemos procesar todas las líneas que se leyeron en el fragmento actual.

Finalmente, el process se llama una vez por línea de entrada. Si está presente, quita el carácter de retorno de carro (para evitar problemas con los finales de línea - LF vs CRLF), y luego llama a JSON.parse uno por la línea. En este punto, puede hacer lo que necesite con su objeto.

Tenga en cuenta que JSON.parse es estricto sobre lo que acepta como entrada; debe citar sus identificadores y valores de cadena con comillas dobles . En otras palabras, {name:''thing1''} lanzará un error; debes usar {"name":"thing1"} .

Debido a que no más de una porción de datos alguna vez estará en la memoria a la vez, esto será extremadamente eficiente en la memoria. También será extremadamente rápido. Una prueba rápida mostró que procesé 10.000 filas en menos de 15 ms.


Si tiene control sobre el archivo de entrada, y es una matriz de objetos, puede resolverlo más fácilmente. Organice para generar el archivo con cada registro en una línea, de esta manera:

[ {"key": value}, {"key": value}, ...

Esto sigue siendo válido JSON.

Luego, use el módulo readline node.js para procesarlos una línea a la vez.

var fs = require("fs"); var lineReader = require(''readline'').createInterface({ input: fs.createReadStream("input.txt") }); lineReader.on(''line'', function (line) { line = line.trim(); if (line.charAt(line.length-1) === '','') { line = line.substr(0, line.length-1); } if (line.charAt(0) === ''{'') { processRecord(JSON.parse(line)); } }); function processRecord(record) { // Process the records one at a time here! }


Tenía un requisito similar, necesito leer un archivo json grande en el nodo js y procesar los datos en fragmentos, llamar a una API y guardarla en mongodb. inputFile.json es como:

{ "customers":[ { /*customer data*/}, { /*customer data*/}, { /*customer data*/}.... ] }

Ahora utilicé JsonStream y EventStream para lograr esto sincrónicamente.

var JSONStream = require(''JSONStream''); var es = require(''event-stream''); fileStream = fs.createReadStream(filePath, {encoding: ''utf8''}); fileStream.pipe(JSONStream.parse(''customers.*'')).pipe(es.through(function (data) { console.log(''printing one customer object read from file ::''); console.log(data); this.pause(); processOneCustomer(data, this); return data; },function end () { console.log(''stream reading ended''); this.emit(''end''); }); function processOneCustomer(data,es){ DataModel.save(function(err,dataModel){ es.resume(); }); }


Resolví este problema usando el módulo split npm . Transmite tu flujo en split, y " Rompe una secuencia y vuelve a armarla para que cada línea sea un pedazo ".

Código de muestra:

var fs = require(''fs'') , split = require(''split'') ; var stream = fs.createReadStream(filePath, {flags: ''r'', encoding: ''utf-8''}); var lineStream = stream.pipe(split()); linestream.on(''data'', function(chunk) { var json = JSON.parse(chunk); // ... });


A partir de octubre de 2014 , puede hacer algo como lo siguiente (usando JSONStream) - https://www.npmjs.org/package/JSONStream

var fs = require(''fs''), JSONStream = require(''JSONStream''), var getStream() = function () { var jsonData = ''myData.json'', stream = fs.createReadStream(jsonData, {encoding: ''utf8''}), parser = JSONStream.parse(''*''); return stream.pipe(parser); } getStream().pipe(MyTransformToDoWhateverProcessingAsNeeded).on(''error'', function (err){ // handle any errors });

Para demostrar con un ejemplo de trabajo:

npm install JSONStream event-stream

data.json:

{ "greeting": "hello world" }

hello.js:

var fs = require(''fs''), JSONStream = require(''JSONStream''), es = require(''event-stream''); var getStream = function () { var jsonData = ''data.json'', stream = fs.createReadStream(jsonData, {encoding: ''utf8''}), parser = JSONStream.parse(''*''); return stream.pipe(parser); }; getStream() .pipe(es.mapSync(function (data) { console.log(data); })); $ node hello.js // hello world