← Torna al Blog

Come Parsare File JSON Grandi Senza Crash nel 2026

Guida completa su come gestire file JSON di grandi dimensioni (100MB+) in JavaScript e Python. Impara tecniche di streaming, parsing progressivo e chunking per evitare errori di memoria.

Big JSON Team15 min di letturaprogramming
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

15 min di lettura

# Come Parsare File JSON Grandi Senza Crash nel 2026

Introduzione

Hai mai incontrato l'errore "FATAL ERROR: JavaScript heap out of memory" cercando di parsare un file JSON grande? Non sei solo. Gestire file JSON grandi (100MB+) è una delle sfide più comuni nello sviluppo web moderno.

In questa guida completa, imparerai tecniche collaudate per parsare file JSON grandi in modo efficiente senza esaurire la memoria o mandare in crash la tua applicazione.

Perché JSON.parse() Fallisce con File Grandi

Il metodo standard JSON.parse() carica l'intero file in memoria in una volta:

// ⚠️ Male per file grandi (>100MB)

const fs = require('fs');

const data = fs.readFileSync('huge-file.json', 'utf8');

const json = JSON.parse(data); // Consuma molta memoria!

Perché fallisce:
  • Memorizza l'intera stringa in memoria (2-3× dimensione file)
  • Crea l'oggetto JavaScript completo (altri 2-5× dimensione file)
  • Nessun rilascio di memoria fino al completamento
  • Totale: Un file da 500MB può usare oltre 2GB di memoria!

Soluzione 1: Streaming con JSONStream (Node.js)

Il miglior approccio per file JSON grandi è lo streaming - elaborare i dati pezzo per pezzo.

Esempio Pratico

const fs = require('fs');

const JSONStream = require('JSONStream');

// Parsare array di oggetti

const stream = fs.createReadStream('large-dataset.json', { encoding: 'utf8' });

const parser = JSONStream.parse(''); // '' per elementi dell'array

let count = 0;

parser.on('data', (item) => {

// Elaborare un elemento alla volta

console.log(Elaborazione record ${++count}:, item.id);

// La tua logica qui - inserire in DB, trasformare, ecc.

if (item.price > 1000) {

saveToDatabase(item);

}

});

parser.on('end', () => {

console.log(Elaborati con successo ${count} record);

});

parser.on('error', (err) => {

console.error('Errore di parsing:', err);

});

stream.pipe(parser);

Parsare Percorsi JSON Annidati

// Per: { "users": [ {...}, {...} ] }

const parser = JSONStream.parse('users.');

// Per: { "data": { "items": [...] } }

const parser = JSONStream.parse('data.items.');

// Per: strutture profonde o condizioni complesse

const parser = JSONStream.parse(['data', 'nested', { emitKey: true }]);

Confronto delle Prestazioni

| Metodo | Dimensione File | Uso Memoria | Tempo |

|--------|----------------|-------------|-------|

| JSON.parse() | 500MB | ~2.1GB 💥 | 8s |

| JSONStream | 500MB | ~45MB ✅ | 12s |

| stream-json | 500MB | ~38MB ✅ | 10s |

Benchmark: Node.js 20, Mac M1, 16GB RAM

Soluzione 2: stream-json (Più Veloce)

stream-json è più veloce di JSONStream e offre più controllo:
const { chain } = require('stream-chain');

const { parser } = require('stream-json');

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

const pipeline = chain([

fs.createReadStream('huge-array.json'),

parser(),

streamArray()

]);

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

console.log(Indice ${key}:, value.name);

// Elaborare qui

processRecord(value);

});

pipeline.on('end', () => console.log('Fatto!'));

Per Oggetti JSON (Non Array)

const { streamObject } = require('stream-json/streamers/StreamObject');

const pipeline = chain([

fs.createReadStream('huge-object.json'),

parser(),

streamObject()

]);

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

console.log(Chiave: ${key}, value);

});

Soluzione 3: Lettura a Blocchi (Browser)

Nei browser, usa FileReader per elaborare file progressivamente:

async function parseHugeJSON(file) {

const chunkSize = 1024 1024; // 1MB

let offset = 0;

let buffer = '';

let results = [];

while (offset < file.size) {

const chunk = file.slice(offset, offset + chunkSize);

const text = await chunk.text();

buffer += text;

// Provare a parsare oggetti completi

const lines = buffer.split('\n');

buffer = lines.pop(); // Mantenere riga incompleta

for (const line of lines) {

if (line.trim()) {

try {

const obj = JSON.parse(line);

results.push(obj);

// Elaborare in lotti per evitare accumulo di memoria

if (results.length >= 1000) {

await processBatch(results);

results = [];

}

} catch (e) {

console.warn('Riga non valida saltata:', line.substring(0, 50));

}

}

}

offset += chunkSize;

// Segnalare progressi

const progress = (offset / file.size 100).toFixed(1);

console.log(Progresso: ${progress}%);

}

// Elaborare ultimo lotto

if (results.length > 0) {

await processBatch(results);

}

}

// Uso

document.getElementById('fileInput').addEventListener('change', async (e) => {

const file = e.target.files[0];

await parseHugeJSON(file);

});

Soluzione 4: Web Workers (Non-Bloccante)

Per prevenire il congelamento dell'UI, sposta il parsing in un Web Worker:

// worker.js

self.onmessage = async function(e) {

const { file } = e.data;

const chunkSize = 1024 1024;

let processed = 0;

for (let offset = 0; offset < file.size; offset += chunkSize) {

const chunk = file.slice(offset, offset + chunkSize);

const text = await chunk.text();

// Elaborare blocco

const lines = text.split('\n').filter(l => l.trim());

const objects = lines.map(line => {

try {

return JSON.parse(line);

} catch {

return null;

}

}).filter(Boolean);

// Rinviare risultati

self.postMessage({

type: 'progress',

data: objects,

percent: (offset / file.size 100)

});

processed += objects.length;

}

self.postMessage({ type: 'complete', total: processed });

};

// main.js

const worker = new Worker('worker.js');

worker.onmessage = function(e) {

if (e.data.type === 'progress') {

console.log(Elaborato: ${e.data.percent.toFixed(1)}%);

updateUI(e.data.data);

} else if (e.data.type === 'complete') {

console.log(Completato! Totale: ${e.data.total});

}

};

// Avviare elaborazione

const fileInput = document.getElementById('upload');

fileInput.addEventListener('change', (e) => {

worker.postMessage({ file: e.target.files[0] });

});

Soluzione 5: Paginazione API

Se recuperi JSON da un'API, usa la paginazione:

async function fetchAllData(apiUrl) {

let allData = [];

let page = 1;

let hasMore = true;

while (hasMore) {

const response = await fetch(${apiUrl}?page=${page}&limit=100);

const data = await response.json();

// Elaborare pagina immediatamente

await processPage(data.items);

allData.push(...data.items);

hasMore = data.hasNextPage;

page++;

// Aggiungere ritardo per evitare limitazione di velocità

await new Promise(resolve => setTimeout(resolve, 100));

}

return allData;

}

// Versione ottimizzata: Non memorizzare tutto

async function processAllData(apiUrl) {

let page = 1;

let hasMore = true;

let totalProcessed = 0;

while (hasMore) {

const response = await fetch(${apiUrl}?page=${page}&limit=100);

const data = await response.json();

// Elaborare senza memorizzare

for (const item of data.items) {

await processItem(item); // Salvare in DB, trasformare, ecc.

totalProcessed++;

}

hasMore = data.hasNextPage;

page++;

console.log(Elaborati ${totalProcessed} elementi finora...);

}

return totalProcessed;

}

Soluzione 6: Python con ijson

Per elaborazione lato server, Python's ijson è eccellente:

import ijson

def process_large_json(filename):

with open(filename, 'rb') as file:

# Parsare array di oggetti

objects = ijson.items(file, 'item')

count = 0

for obj in objects:

# Elaborare ogni elemento

if obj.get('price', 0) > 1000:

save_to_database(obj)

count += 1

if count % 1000 == 0:

print(f"Elaborati {count} record...")

print(f"Totale: {count} record")

# Per strutture annidate

def process_nested(filename):

with open(filename, 'rb') as file:

# Estrarre solo chiavi specifiche

for user_id in ijson.items(file, 'users.item.id'):

print(f"ID utente: {user_id}")

Strategie di Ottimizzazione

1. Elaborazione a Lotti Invece che Singola

let batch = [];

const BATCH_SIZE = 1000;

parser.on('data', async (item) => {

batch.push(item);

if (batch.length >= BATCH_SIZE) {

await insertBatch(batch); // Un inserimento per lotto

batch = [];

}

});

parser.on('end', async () => {

if (batch.length > 0) {

await insertBatch(batch); // Elaborare ultimo lotto

}

});

2. Filtrare Dati in Anticipo

const { pick } = require('stream-json/filters/Pick');

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

const pipeline = chain([

fs.createReadStream('data.json'),

parser(),

pick({ filter: 'users' }), // Solo proprietà "users"

streamArray()

]);

3. Compressione File

const zlib = require('zlib');

const pipeline = chain([

fs.createReadStream('data.json.gz'),

zlib.createGunzip(), // Decomprimere al volo

parser(),

streamArray()

]);

Scenari Comuni e Soluzioni

Scenario 1: Importazione Database da JSON

const { chain } = require('stream-chain');

const { parser } = require('stream-json');

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

const { MongoClient } = require('mongodb');

async function importToMongo(filename, collectionName) {

const client = await MongoClient.connect('mongodb://localhost:27017');

const collection = client.db('mydb').collection(collectionName);

let batch = [];

const BATCH_SIZE = 1000;

const pipeline = chain([

fs.createReadStream(filename),

parser(),

streamArray()

]);

for await (const { value } of pipeline) {

batch.push(value);

if (batch.length >= BATCH_SIZE) {

await collection.insertMany(batch);

console.log(Inseriti ${batch.length} documenti);

batch = [];

}

}

if (batch.length > 0) {

await collection.insertMany(batch);

}

await client.close();

}

Scenario 2: Convertire JSON in CSV

const { Transform } = require('stream');

const csvWriter = require('csv-write-stream');

const jsonToCSV = new Transform({

objectMode: true,

transform(chunk, encoding, callback) {

// Convertire oggetto JSON in riga CSV

callback(null, {

id: chunk.id,

name: chunk.name,

email: chunk.email

});

}

});

const writer = csvWriter();

writer.pipe(fs.createWriteStream('output.csv'));

pipeline

.pipe(jsonToCSV)

.pipe(writer);

Scenario 3: Elaborare JSON Delimitato da Newline (NDJSON)

const readline = require('readline');

async function processNDJSON(filename) {

const fileStream = fs.createReadStream(filename);

const rl = readline.createInterface({

input: fileStream,

crlfDelay: Infinity

});

let count = 0;

for await (const line of rl) {

if (line.trim()) {

const obj = JSON.parse(line);

await processObject(obj);

count++;

}

}

console.log(Elaborate ${count} righe);

}

Risoluzione Problemi

Errore: JavaScript heap out of memory

Soluzione:
# Aumentare limite memoria in Node.js

node --max-old-space-size=4096 script.js

Ma meglio: Usa lo streaming invece!

Errore: JSON Non Valido con Lettura a Blocchi

Problema: I blocchi possono dividere oggetti JSON. Soluzione: Usa JSON delimitato da linee (NDJSON):
// Prima convertire JSON in NDJSON

const items = require('./data.json'); // Caricare piccolo una volta

const stream = fs.createWriteStream('data.ndjson');

items.forEach(item => {

stream.write(JSON.stringify(item) + '\n');

});

stream.end();

Errore: Processo Troppo Lento

Ottimizzazioni:
  • Usa stream-json invece di JSONStream (2× più veloce)
  • Aumentare dimensione lotto (1000 → 5000)
  • Parallelizzare inserimenti DB
  • Usare pool di connessioni DB
  • Aggiungere indici prima dell'inserimento di massa
  • Strumenti Consigliati

    Per Validazione JSON

    Usa BigJSON.online per validare e ispezionare rapidamente file JSON grandi senza caricarli completamente. Supporta streaming e validazione in tempo reale.

    Librerie

    • Node.js: stream-json (il più veloce), JSONStream (il più semplice)
    • Python: ijson (il migliore)
    • Browser: FileReader API + Web Workers

    Conclusione

    Raccomandazioni Rapide:

    | Caso d'Uso | Migliore Soluzione | Uso Memoria |

    |------------|-------------------|-------------|

    | Node.js (array grandi) | stream-json | Molto basso |

    | Node.js (strutture semplici) | JSONStream | Basso |

    | Browser (< 50MB) | Lettura a blocchi | Medio |

    | Browser (> 50MB) | Web Workers | Basso |

    | Download API | Paginazione + elaborazione streaming | Molto basso |

    | Python | ijson | Molto basso |

    Punti Chiave:
    • Usa sempre lo streaming per file > 50MB
    • Elaborazione a lotti per inserimenti DB
    • Filtrare in anticipo per ridurre uso memoria
    • Non usare JSON.parse() per file grandi
    • Non caricare tutto in una volta in memoria

    Usa pacchetti di streaming moderni e potrai gestire file JSON di dimensioni GB facilmente senza esaurire la memoria o andare in crash.

    Risorse Aggiuntive

    Hai trovato utile questa guida? Condividila con i tuoi colleghi sviluppatori che lottano con file JSON grandi!

    Share:

    Articoli Correlati

    Read in English