alltools.one
JSON‱
2025-06-02
‱
8 min
‱
alltools.one Team
JSONStreamingPerformanceLarge FilesMemory

Streaming JSON pour les gros fichiers : Traiter sans tout charger

Le parsing JSON standard charge l'intĂ©gralitĂ© du document en mĂ©moire, construit une structure de donnĂ©es complĂšte, puis vous donne accĂšs. Pour un fichier de 10 Mo, ça fonctionne bien. Pour un fichier de 10 Go, votre processus manque de mĂ©moire et plante. Les parseurs en streaming rĂ©solvent ce problĂšme en traitant le JSON de maniĂšre incrĂ©mentale — lisant et traitant les donnĂ©es au fur et Ă  mesure de leur arrivĂ©e, sans jamais conserver l'intĂ©gralitĂ© du document en mĂ©moire.

Le problĂšme du parsing standard

import json

# Ceci charge le fichier ENTIER en mémoire
with open('huge.json') as f:
    data = json.load(f)  # 10 GB file = 10+ GB of RAM

# Le traitement se fait aprĂšs le chargement complet
for item in data['records']:
    process(item)

Pour un fichier avec 1 million d'enregistrements à 10 Ko chacun, le parsing standard nécessite :

  • Taille du fichier : ~10 Go
  • MĂ©moire pour le parsing : ~10 Go (la chaĂźne brute)
  • MĂ©moire pour la structure de donnĂ©es : ~15-20 Go (les objets Python sont plus volumineux que le JSON brut)
  • Total : ~25-30 Go de RAM

Le streaming réduit cela à quelques mégaoctets.

Approches de streaming

Style SAX (basé sur les événements)

Le parseur émet des événements lorsqu'il rencontre des tokens JSON :

import ijson

# Traiter les éléments un par un - utilisation mémoire constante
with open('huge.json', 'rb') as f:
    for record in ijson.items(f, 'records.item'):
        process(record)  # Each record is parsed individually
        # Previous records are garbage collected

Les événements incluent : start_map, map_key, end_map, start_array, end_array, string, number, boolean, null.

JSON Lines (JSONL / NDJSON)

Une approche plus simple : un objet JSON par ligne. Chaque ligne est un document JSON complet et valide :

{"id": 1, "name": "Alice", "email": "alice@example.com"}
{"id": 2, "name": "Bob", "email": "bob@example.com"}
{"id": 3, "name": "Charlie", "email": "charlie@example.com"}

Le traitement est trivial — lire ligne par ligne :

with open('data.jsonl') as f:
    for line in f:
        record = json.loads(line)
        process(record)

Avantages de JSON Lines :

  • Chaque ligne est analysable indĂ©pendamment (traitement parallĂšle)
  • Compatible avec l'ajout (il suffit d'ajouter une nouvelle ligne)
  • Fonctionne avec les outils Unix standard (grep, wc, head, tail)
  • Naturel pour les fichiers de log et les flux de donnĂ©es

Traitement par morceaux

Pour les tableaux JSON standard, divisez le traitement en morceaux :

import ijson

def process_in_chunks(filename, chunk_size=1000):
    chunk = []
    with open(filename, 'rb') as f:
        for record in ijson.items(f, 'item'):
            chunk.append(record)
            if len(chunk) >= chunk_size:
                process_batch(chunk)
                chunk = []
    if chunk:
        process_batch(chunk)

Implémentations par langage

Python (ijson)

import ijson

# Streaming depuis un fichier
with open('large.json', 'rb') as f:
    parser = ijson.parse(f)
    for prefix, event, value in parser:
        if prefix == 'records.item.name':
            print(value)

# Streaming depuis une réponse HTTP
import urllib.request
response = urllib.request.urlopen('https://api.example.com/data')
for record in ijson.items(response, 'records.item'):
    process(record)

JavaScript (Node.js)

const { createReadStream } = require('fs');
const { parser } = require('stream-json');
const { streamArray } = require('stream-json/streamers/StreamArray');

const pipeline = createReadStream('large.json')
  .pipe(parser())
  .pipe(streamArray());

pipeline.on('data', ({ value }) => {
  process(value);
});

pipeline.on('end', () => {
  console.log('Done processing');
});

Ligne de commande (jq)

# Mode streaming - traiter les objets individuellement
jq --stream 'select(length == 2) | .[1]' large.json

# Traiter JSON Lines
cat data.jsonl | jq -c 'select(.age > 30)'

# Convertir un tableau en JSON Lines
jq -c '.[]' large_array.json > data.jsonl

Quand utiliser le streaming

ScénarioStandardStreaming
Fichier de moins de 100 MoPréféréExcessif
Fichier de 100 Mo à 1 GoDépend de la RAMRecommandé
Fichier de plus de 1 GoNon viableRequis
Réponse HTTP (volumineuse)Risque de timeoutStreamer à la réception
Flux de données en temps réelNon applicableRequis
Lecture unique simplePréféréInutile

Comparaison des performances

Traitement de 1 million d'enregistrements (fichier de 1 Go) :

ApprocheUtilisation mémoireTemps de traitementComplexité
json.load()3-5 Go15 secSimple
ijson streaming50 Mo45 secModéré
JSON Lines10 Mo12 secSimple
Par morceaux (1000)100 Mo20 secModéré

Le streaming utilise beaucoup moins de mémoire mais est plus lent pour le parsing de style SAX en raison de la surcharge événementielle. JSON Lines est à la fois rapide et économe en mémoire car chaque ligne est une opération de parsing indépendante.

Bonnes pratiques

  1. Utilisez JSON Lines quand c'est possible : C'est le format de streaming le plus simple et il fonctionne avec les outils standard.
  2. Configurez les tailles de tampon : Configurez les tampons de lecture pour un débit optimal (64 Ko à 1 Mo typiquement).
  3. Traitez par lots : Regroupez les insertions en base de données et les appels API plutÎt que de traiter un enregistrement à la fois.
  4. Gérez les erreurs avec grùce : En streaming, un enregistrement malformé ne devrait pas faire planter l'ensemble du pipeline.
  5. Surveillez la mémoire : Utilisez le profilage pour vérifier que le streaming maintient effectivement la mémoire bornée.

Pour formater et valider des fichiers JSON plus petits, notre Formateur JSON gÚre les documents jusqu'à 100 Mo avec un formatage en temps réel.

FAQ

Puis-je utiliser JSONPath avec des parseurs en streaming ?

Certaines bibliothĂšques de streaming supportent le filtrage basĂ© sur le chemin. Python ijson.items supporte le filtrage par chemin pendant le streaming. Cependant, les requĂȘtes JSONPath complexes (filtres, jokers Ă  travers les niveaux) nĂ©cessitent gĂ©nĂ©ralement le document complet en mĂ©moire. Pour les requĂȘtes basĂ©es sur le chemin, consultez notre Guide JSONPath.

Comment convertir un gros tableau JSON en JSON Lines ?

Utilisez jq -c '.[]' input.json > output.jsonl pour les fichiers qui tiennent en mémoire. Pour les fichiers vraiment volumineux, utilisez un convertisseur en streaming : lisez le tableau avec un parseur en streaming et écrivez chaque élément comme une ligne.

Ressources connexes

Published on 2025-06-02
JSON Streaming for Large Files: Process Without Loading All | alltools.one