Cómo Parsear Archivos JSON Grandes Sin Colapsar en 2026
Guía completa sobre cómo manejar archivos JSON grandes (100MB+) en JavaScript y Python. Aprende técnicas de streaming, parseo progresivo y fragmentación para evitar errores de memoria.
Big JSON Team
• Technical WriterExpert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.
# Cómo Parsear Archivos JSON Grandes Sin Colapsar en 2026
Introducción
¿Alguna vez has encontrado el error "FATAL ERROR: JavaScript heap out of memory" al intentar parsear un archivo JSON grande? No estás solo. Manejar archivos JSON grandes (100MB+) es uno de los desafíos más comunes en el desarrollo web moderno.
En esta guía completa, aprenderás técnicas probadas para parsear archivos JSON grandes de manera eficiente sin agotar la memoria ni colapsar tu aplicación.
Por Qué JSON.parse() Falla con Archivos Grandes
El método estándar JSON.parse() carga todo el archivo en memoria de una sola vez:
// ⚠️ Malo para archivos grandes (>100MB)
const fs = require('fs');
const data = fs.readFileSync('huge-file.json', 'utf8');
const json = JSON.parse(data); // ¡Consume mucha memoria!
Por qué falla:
- Almacena toda la cadena en memoria (2-3× tamaño del archivo)
- Crea el objeto JavaScript completo (otros 2-5× tamaño del archivo)
- No hay liberación de memoria hasta completar
- Total: ¡Un archivo de 500MB puede usar más de 2GB de memoria!
Solución 1: Streaming con JSONStream (Node.js)
El mejor enfoque para archivos JSON grandes es streaming - procesar datos pieza por pieza.
Ejemplo Práctico
const fs = require('fs');
const JSONStream = require('JSONStream');
// Parsear array de objetos
const stream = fs.createReadStream('large-dataset.json', { encoding: 'utf8' });
const parser = JSONStream.parse(''); // '' para elementos del array
let count = 0;
parser.on('data', (item) => {
// Procesar un elemento a la vez
console.log(Procesando registro ${++count}:, item.id);
// Tu lógica aquí - insertar en BD, transformar, etc.
if (item.price > 1000) {
saveToDatabase(item);
}
});
parser.on('end', () => {
console.log(Procesados exitosamente ${count} registros);
});
parser.on('error', (err) => {
console.error('Error de parseo:', err);
});
stream.pipe(parser);
Parsear Rutas JSON Anidadas
// Para: { "users": [ {...}, {...} ] }
const parser = JSONStream.parse('users.');
// Para: { "data": { "items": [...] } }
const parser = JSONStream.parse('data.items.');
// Para: estructuras profundas o condiciones complejas
const parser = JSONStream.parse(['data', 'nested', { emitKey: true }]);
Comparación de Rendimiento
| Método | Tamaño Archivo | Uso de Memoria | Tiempo |
|--------|---------------|----------------|--------|
| JSON.parse() | 500MB | ~2.1GB 💥 | 8s |
| JSONStream | 500MB | ~45MB ✅ | 12s |
| stream-json | 500MB | ~38MB ✅ | 10s |
Benchmark: Node.js 20, Mac M1, 16GB RAMSolución 2: stream-json (Más Rápido)
stream-json es más rápido que JSONStream y ofrece más control:
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(Índice ${key}:, value.name);
// Procesar aquí
processRecord(value);
});
pipeline.on('end', () => console.log('¡Listo!'));
Para Objetos JSON (No Arrays)
const { streamObject } = require('stream-json/streamers/StreamObject');
const pipeline = chain([
fs.createReadStream('huge-object.json'),
parser(),
streamObject()
]);
pipeline.on('data', ({ key, value }) => {
console.log(Clave: ${key}, value);
});
Solución 3: Lectura por Fragmentos (Navegador)
En navegadores, usa FileReader para procesar archivos progresivamente:
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;
// Intentar parsear objetos completos
const lines = buffer.split('\n');
buffer = lines.pop(); // Mantener línea incompleta
for (const line of lines) {
if (line.trim()) {
try {
const obj = JSON.parse(line);
results.push(obj);
// Procesar en lotes para evitar acumulación de memoria
if (results.length >= 1000) {
await processBatch(results);
results = [];
}
} catch (e) {
console.warn('Omitiendo línea inválida:', line.substring(0, 50));
}
}
}
offset += chunkSize;
// Reportar progreso
const progress = (offset / file.size 100).toFixed(1);
console.log(Progreso: ${progress}%);
}
// Procesar último lote
if (results.length > 0) {
await processBatch(results);
}
}
// Uso
document.getElementById('fileInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
await parseHugeJSON(file);
});
Solución 4: Web Workers (No Bloqueante)
Para prevenir que la UI se congele, mueve el parseo a 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();
// Procesar fragmento
const lines = text.split('\n').filter(l => l.trim());
const objects = lines.map(line => {
try {
return JSON.parse(line);
} catch {
return null;
}
}).filter(Boolean);
// Enviar resultados de vuelta
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(Procesado: ${e.data.percent.toFixed(1)}%);
updateUI(e.data.data);
} else if (e.data.type === 'complete') {
console.log(¡Completado! Total: ${e.data.total});
}
};
// Iniciar procesamiento
const fileInput = document.getElementById('upload');
fileInput.addEventListener('change', (e) => {
worker.postMessage({ file: e.target.files[0] });
});
Solución 5: Paginación de API
Si obtienes JSON de una API, usa paginación:
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();
// Procesar página inmediatamente
await processPage(data.items);
allData.push(...data.items);
hasMore = data.hasNextPage;
page++;
// Añadir delay para evitar límites de tasa
await new Promise(resolve => setTimeout(resolve, 100));
}
return allData;
}
// Versión optimizada: No almacenar todo
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();
// Procesar sin almacenar
for (const item of data.items) {
await processItem(item); // Guardar en BD, transformar, etc.
totalProcessed++;
}
hasMore = data.hasNextPage;
page++;
console.log(Procesados ${totalProcessed} elementos hasta ahora...);
}
return totalProcessed;
}
Solución 6: Python con ijson
Para procesamiento del lado del servidor, Python's ijson es excelente:
import ijson
def process_large_json(filename):
with open(filename, 'rb') as file:
# Parsear array de objetos
objects = ijson.items(file, 'item')
count = 0
for obj in objects:
# Procesar cada elemento
if obj.get('price', 0) > 1000:
save_to_database(obj)
count += 1
if count % 1000 == 0:
print(f"Procesados {count} registros...")
print(f"Total: {count} registros")
# Para estructuras anidadas
def process_nested(filename):
with open(filename, 'rb') as file:
# Extraer solo claves específicas
for user_id in ijson.items(file, 'users.item.id'):
print(f"ID de usuario: {user_id}")
Estrategias de Optimización
1. Procesamiento por Lotes en Lugar de Individual
let batch = [];
const BATCH_SIZE = 1000;
parser.on('data', async (item) => {
batch.push(item);
if (batch.length >= BATCH_SIZE) {
await insertBatch(batch); // Una inserción por lote
batch = [];
}
});
parser.on('end', async () => {
if (batch.length > 0) {
await insertBatch(batch); // Procesar último lote
}
});
2. Filtrar Datos Temprano
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 propiedad "users"
streamArray()
]);
3. Compresión de Archivos
const zlib = require('zlib');
const pipeline = chain([
fs.createReadStream('data.json.gz'),
zlib.createGunzip(), // Descomprimir al vuelo
parser(),
streamArray()
]);
Escenarios Comunes y Soluciones
Escenario 1: Importación de Base de Datos desde 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(Insertados ${batch.length} documentos);
batch = [];
}
}
if (batch.length > 0) {
await collection.insertMany(batch);
}
await client.close();
}
Escenario 2: Convertir JSON a CSV
const { Transform } = require('stream');
const csvWriter = require('csv-write-stream');
const jsonToCSV = new Transform({
objectMode: true,
transform(chunk, encoding, callback) {
// Convertir objeto JSON a fila 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);
Escenario 3: Procesar JSON Delimitado por Líneas (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(Procesadas ${count} líneas);
}
Solución de Problemas
Error: JavaScript heap out of memory
Solución:# Aumentar límite de memoria en Node.js
node --max-old-space-size=4096 script.js
Pero mejor: ¡Usa streaming en su lugar!
Error: JSON Inválido al Leer por Fragmentos
Problema: Los fragmentos pueden dividir objetos JSON. Solución: Usa JSON delimitado por líneas (NDJSON):// Primero convertir JSON a NDJSON
const items = require('./data.json'); // Cargar pequeño una vez
const stream = fs.createWriteStream('data.ndjson');
items.forEach(item => {
stream.write(JSON.stringify(item) + '\n');
});
stream.end();
Error: Proceso Demasiado Lento
Optimizaciones:stream-json en lugar de JSONStream (2× más rápido)Herramientas Recomendadas
Para Validación de JSON
Usa BigJSON.online para validar e inspeccionar rápidamente archivos JSON grandes sin cargarlos completamente. Soporta streaming y validación en tiempo real.
Bibliotecas
- Node.js:
stream-json(más rápido),JSONStream(más simple) - Python:
ijson(el mejor) - Navegador: FileReader API + Web Workers
Conclusión
Recomendaciones Rápidas:| Caso de Uso | Mejor Solución | Uso de Memoria |
|-------------|---------------|----------------|
| Node.js (arrays grandes) | stream-json | Muy bajo |
| Node.js (estructuras simples) | JSONStream | Bajo |
| Navegador (< 50MB) | Lectura por fragmentos | Medio |
| Navegador (> 50MB) | Web Workers | Bajo |
| Descarga API | Paginación + procesamiento streaming | Muy bajo |
| Python | ijson | Muy bajo |
Puntos Clave:- ✅ Siempre usa streaming para archivos > 50MB
- ✅ Procesamiento por lotes para inserciones en BD
- ✅ Filtrar temprano para reducir uso de memoria
- ❌ No uses JSON.parse() para archivos grandes
- ❌ No cargues todo de una vez en memoria
Usa paquetes de streaming modernos y podrás manejar archivos JSON de tamaño GB fácilmente sin agotar memoria ni colapsar.
Recursos Adicionales
- Trabajar con Archivos JSON Grandes
- JavaScript y JSON: Prácticas Avanzadas
- JSON en Ciencia de Datos
- Validación de JSON en Línea
¿Encontraste útil esta guía? ¡Compártela con tus compañeros desarrolladores que luchan con archivos JSON grandes!