← Volver al Blog

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 Team15 min de lecturaprogramming
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 lectura

# 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 RAM

Solució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:
  • Usa stream-json en lugar de JSONStream (2× más rápido)
  • Aumentar tamaño de lote (1000 → 5000)
  • Paralelizar inserciones en BD
  • Usar pools de conexión de BD
  • Agregar índices antes de inserción masiva
  • 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

    ¿Encontraste útil esta guía? ¡Compártela con tus compañeros desarrolladores que luchan con archivos JSON grandes!

    Share:

    Artículos Relacionados

    Read in English