J'ai un fichier qui stocke de nombreux objets JavaScript sous forme JSON et je dois lire le fichier, créer chacun des objets et faire quelque chose avec eux (insérez-les dans une base de données dans mon cas). Les objets JavaScript peuvent être représentés par un format:
Format A:
[{name: 'thing1'},
....
{name: 'thing999999999'}]
ou Format B:
{name: 'thing1'} // <== My choice.
...
{name: 'thing999999999'}
Notez que le ...
indique un grand nombre d'objets JSON. Je suis conscient que je pourrais lire l'intégralité du fichier en mémoire, puis l'utiliser JSON.parse()
comme ceci:
fs.readFile(filePath, 'utf-8', function (err, fileContents) {
if (err) throw err;
console.log(JSON.parse(fileContents));
});
Cependant, le fichier peut être très volumineux, je préférerais utiliser un flux pour ce faire. Le problème que je vois avec un flux est que le contenu du fichier peut être divisé en morceaux de données à tout moment, alors comment puis-je l'utiliser JSON.parse()
sur de tels objets?
Idéalement, chaque objet serait lu comme un bloc de données distinct, mais je ne sais pas comment faire cela .
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!");
});*/
Remarque, je souhaite empêcher la lecture du fichier entier en mémoire. L'efficacité du temps n'a pas d'importance pour moi. Oui, je pourrais essayer de lire un certain nombre d'objets à la fois et de les insérer tous à la fois, mais c'est un ajustement des performances - j'ai besoin d'un moyen qui garantit de ne pas causer de surcharge de mémoire, quel que soit le nombre d'objets contenus dans le fichier .
Je peux choisir d'utiliser FormatA
ou FormatB
ou peut-être autre chose, veuillez simplement le préciser dans votre réponse. Merci!
Réponses:
Pour traiter un fichier ligne par ligne, il vous suffit de découpler la lecture du fichier et le code qui agit sur cette entrée. Vous pouvez accomplir cela en tamponnant votre entrée jusqu'à ce que vous atteigniez une nouvelle ligne. En supposant que nous ayons un objet JSON par ligne (en gros, le format B):
Chaque fois que le flux de fichiers reçoit des données du système de fichiers, il est stocké dans une mémoire tampon, puis
pump
est appelé.S'il n'y a pas de nouvelle ligne dans le tampon,
pump
retourne simplement sans rien faire. Plus de données (et potentiellement une nouvelle ligne) seront ajoutées au tampon la prochaine fois que le flux obtiendra des données, puis nous aurons un objet complet.S'il y a une nouvelle ligne,
pump
coupe la mémoire tampon du début à la nouvelle ligne et la transmet àprocess
. Il vérifie ensuite à nouveau s'il y a une autre nouvelle ligne dans le tampon (lawhile
boucle). De cette façon, nous pouvons traiter toutes les lignes qui ont été lues dans le bloc courant.Enfin,
process
est appelé une fois par ligne d'entrée. S'il est présent, il supprime le caractère de retour chariot (pour éviter les problèmes de fin de ligne - LF vs CRLF), puis appelleJSON.parse
une ligne. À ce stade, vous pouvez faire tout ce dont vous avez besoin avec votre objet.Notez que
JSON.parse
c'est strictement ce qu'il accepte comme entrée; vous devez citer vos identifiants et valeurs de chaîne avec des guillemets doubles . En d'autres termes,{name:'thing1'}
jettera une erreur; vous devez utiliser{"name":"thing1"}
.Parce que pas plus d'un morceau de données ne sera jamais en mémoire à la fois, ce sera extrêmement efficace en mémoire. Ce sera également extrêmement rapide. Un test rapide a montré que j'avais traité 10 000 lignes en moins de 15 ms.
la source
Tout comme je pensais que ce serait amusant d'écrire un analyseur JSON en streaming, j'ai également pensé que je devrais peut-être faire une recherche rapide pour voir s'il y en avait déjà un disponible.
Il s'avère qu'il y en a.
Depuis que je viens de le trouver, je ne l'ai évidemment pas utilisé, donc je ne peux pas faire de commentaire sur sa qualité, mais je serai intéressé de savoir si cela fonctionne.
Cela fonctionne avec le Javascript suivant et
_.isString
:Cela enregistrera les objets au fur et à mesure qu'ils entrent si le flux est un tableau d'objets. Par conséquent, la seule chose mise en mémoire tampon est un objet à la fois.
la source
Depuis octobre 2014 , vous pouvez simplement faire quelque chose comme ce qui suit (en utilisant JSONStream) - https://www.npmjs.org/package/JSONStream
Pour démontrer avec un exemple de travail:
data.json:
bonjour.js:
la source
parse('*')
ou vous n'obtiendrez aucune donnée.var getStream() = function () {
doit être supprimé.Je me rends compte que vous voulez éviter de lire tout le fichier JSON en mémoire si possible, mais si vous avez la mémoire disponible, cela peut ne pas être une mauvaise idée en termes de performances. L'utilisation de require () de node.js sur un fichier json charge les données en mémoire très rapidement.
J'ai exécuté deux tests pour voir à quoi ressemblaient les performances lors de l'impression d'un attribut de chaque fonctionnalité à partir d'un fichier geojson de 81 Mo.
Dans le premier test, j'ai lu l'intégralité du fichier geojson en mémoire en utilisant
var data = require('./geo.json')
. Cela a pris 3330 millisecondes, puis l'impression d'un attribut de chaque entité a pris 804 millisecondes pour un total de 4134 millisecondes. Cependant, il est apparu que node.js utilisait 411 Mo de mémoire.Dans le deuxième test, j'ai utilisé la réponse de @ arcseldon avec JSONStream + event-stream. J'ai modifié la requête JSONPath pour sélectionner uniquement ce dont j'avais besoin. Cette fois, la mémoire n'a jamais dépassé 82 Mo, cependant, le tout a maintenant pris 70 secondes pour se terminer!
la source
J'avais une exigence similaire, je dois lire un gros fichier json dans le nœud js et traiter les données en morceaux, appeler une api et enregistrer dans mongodb. inputFile.json est comme:
Maintenant, j'ai utilisé JsonStream et EventStream pour y parvenir de manière synchrone.
la source
J'ai écrit un module qui peut faire cela, appelé BFJ . Plus précisément, la méthode
bfj.match
peut être utilisée pour diviser un gros flux en morceaux discrets de JSON:Ici,
bfj.match
retourne un flux en mode objet lisible qui recevra les éléments de données analysés et reçoit 3 arguments:Un flux lisible contenant le JSON d'entrée.
Un prédicat qui indique quels éléments du JSON analysé seront poussés vers le flux de résultats.
Un objet d'options indiquant que l'entrée est JSON délimitée par une nouvelle ligne (il s'agit de traiter le format B de la question, ce n'est pas requis pour le format A).
Une fois appelé,
bfj.match
analysera JSON à partir du flux d'entrée en profondeur en premier, en appelant le prédicat avec chaque valeur pour déterminer s'il faut ou non pousser cet élément vers le flux de résultats. Le prédicat reçoit trois arguments:La clé de propriété ou l'index du tableau (ce sera
undefined
pour les éléments de niveau supérieur).La valeur elle-même.
La profondeur de l'élément dans la structure JSON (zéro pour les éléments de niveau supérieur).
Bien entendu, un prédicat plus complexe peut également être utilisé selon les besoins. Vous pouvez également transmettre une chaîne ou une expression régulière au lieu d'une fonction de prédicat, si vous souhaitez effectuer des correspondances simples avec des clés de propriété.
la source
J'ai résolu ce problème en utilisant le module split npm . Dirigez votre flux en deux, et il "décomposera un flux et réassemblez-le pour que chaque ligne soit un morceau ".
Exemple de code:
la source
Si vous contrôlez le fichier d'entrée et qu'il s'agit d'un tableau d'objets, vous pouvez résoudre ce problème plus facilement. Arrangez-vous pour sortir le fichier avec chaque enregistrement sur une ligne, comme ceci:
C'est toujours du JSON valide.
Ensuite, utilisez le module readline node.js pour les traiter une ligne à la fois.
la source
Je pense que vous devez utiliser une base de données. MongoDB est un bon choix dans ce cas car il est compatible JSON.
MISE À JOUR : Vous pouvez utiliser l' outil mongoimport pour importer des données JSON dans MongoDB.
la source