StackPractices
beginner Por Mathias Paulenko

Convertir JSON a CSV

Cómo convertir datos JSON a formato CSV en Python, Java y JavaScript: aplana objetos anidados, desenrolla arrays y genera salida lista para Excel.

Temas: data

Visión General

Convertir JSON a CSV conecta las respuestas estructuradas de las APIs con formatos compatibles con hojas de cálculo. Impulsa exportaciones de datos, pipelines de BI e interoperabilidad con flujos de trabajo basados en Excel. El verdadero trabajo no es el cambio de formato, sino aplanar la estructura anidada de JSON en filas y columnas sin perder datos. Para la operación inversa, consulta Convertir CSV a JSON.

Cuándo Usar

Usa este recurso cuando:

  • Exportes datos de respuestas de API a Excel o Google Sheets para perfiles no técnicos
  • Alimentes pipelines ETL que cargan en herramientas de BI o data warehouses
  • Generes informes desde bases de datos NoSQL que almacenan documentos JSON
  • Compartas análisis puntuales con equipos que trabajan en hojas de cálculo

Solución

Python

import json
import csv

# Array JSON plano: cada objeto se convierte en una fila
json_data = '[{"name":"Alice","age":30},{"name":"Bob","age":25}]'
records = json.loads(json_data)

with open('output.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.DictWriter(f, fieldnames=records[0].keys())
    writer.writeheader()
    writer.writerows(records)
# Aplanar JSON anidado con pandas
# pip install pandas
import json
import pandas as pd

nested = '[{"user":{"name":"Alice"},"orders":[{"id":1}]}]'
df = pd.json_normalize(json.loads(nested), sep='.')
df.to_csv('output.csv', index=False)
# JSON Lines (un objeto por línea) a CSV
import json
import csv

with open('data.jsonl', encoding='utf-8') as src, open('output.csv', 'w', newline='', encoding='utf-8') as out:
    records = (json.loads(line) for line in src if line.strip())
    first = next(records)
    writer = csv.DictWriter(out, fieldnames=first.keys())
    writer.writeheader()
    writer.writerow(first)
    writer.writerows(records)
# Convertir archivos grandes en streaming sin cargarlos en memoria
# pip install ijson
import csv
import ijson

# 'item' devuelve cada objeto dentro de un array JSON de primer nivel
with open('big.json', 'rb') as src, open('output.csv', 'w', newline='', encoding='utf-8') as out:
    writer = None
    for record in ijson.items(src, 'item'):
        if writer is None:
            writer = csv.DictWriter(out, fieldnames=record.keys())
            writer.writeheader()
        writer.writerow(record)

La versión en streaming asume claves consistentes entre registros. Para estructuras irregulares, recoge la unión de claves en una primera pasada y pásala como fieldnames.

JavaScript

// Conversión manual para arrays planos
const records = [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }];
const headers = Object.keys(records[0]);
const rows = records.map(r => headers.map(h => JSON.stringify(r[h])).join(','));
const csv = [headers.join(','), ...rows].join('\n');
console.log(csv);
// Usando @json2csv para una conversión fiable
// npm install @json2csv/plainjs
import { Parser } from '@json2csv/plainjs';

const parser = new Parser();
const csv = parser.parse(records);
console.log(csv);

Java

// Jackson + commons-csv
// Maven: com.fasterxml.jackson.core:jackson-databind, org.apache.commons:commons-csv
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;

public class JsonToCsv {
    public static void main(String[] args) throws Exception {
        String json = "[{\"name\":\"Alice\",\"age\":30},{\"name\":\"Bob\",\"age\":25}]";
        ObjectMapper mapper = new ObjectMapper();
        List<Map<String, Object>> records = mapper.readValue(json, List.class);

        StringWriter sw = new StringWriter();
        try (CSVPrinter printer = new CSVPrinter(sw, CSVFormat.DEFAULT.withHeader("name", "age"))) {
            for (Map<String, Object> record : records) {
                printer.printRecord(record.get("name"), record.get("age"));
            }
        }
        System.out.println(sw.toString());
    }
}

Explicación

El desafío principal es aplanar datos jerárquicos en una tabla bidimensional. Los arrays JSON planos se mapean directamente a filas. Los objetos anidados necesitan una estrategia: aplanar claves en columnas con notación de punto (user.name), desenrollar (unwind) arrays en filas repetidas, o dividir entidades relacionadas en varios CSV vinculados por una columna ID — el mismo trade-off que la normalización en bases de datos.

Mermaid flowchart LR diagram

Dos detalles importan en la práctica:

  • Derivación de cabeceras: records[0].keys() solo captura los campos del primer registro. Si registros posteriores traen claves extra o faltantes, las columnas se pierden o desalinean. Calcula la unión de claves de todos los registros antes de escribir.
  • Codificación: escribe UTF-8 explícito y antepone un BOM (\ufeff) cuando el archivo vaya a Excel, o los caracteres no-ASCII se verán corruptos.

Para registros irregulares, la versión defensiva recoge primero la unión de claves y rellena los huecos con un valor por defecto:

fieldnames = sorted({key for r in records for key in r})
writer = csv.DictWriter(f, fieldnames=fieldnames, restval='', extrasaction='ignore')
writer.writeheader()
writer.writerows(records)

El restval='' deja los campos ausentes como celdas vacías en lugar de lanzar error, y extrasaction='ignore' protege ante claves que no se hayan capturado en la pasada de unión.

pandas.json_normalize aplana automáticamente, con las opciones record_path y meta para arrays. @json2csv soporta campos personalizados, transforms y unwind para arrays. Java no incluye un conversor JSON→CSV, así que iteras manualmente con Jackson más una librería CSV como commons-csv o univocity-parsers.

Ten en cuenta que CSV es un formato de texto: números, booleanos, nulos y fechas se convierten en cadenas, y quien lea el archivo tendrá que inferir los tipos de nuevo. Si importa la fidelidad de ida y vuelta, serializa los valores con una convención explícita (fechas ISO 8601, null como celda vacía) y documéntala para los consumidores.

Variantes

TecnologíaLibreríaEnfoqueNotas
Pythoncsv (stdlib)DictWriterCero dependencias, requiere JSON plano
Pythonpandasjson_normalize()Maneja anidación; dependencia pesada
Pythonijson + csvParseo en streamingMemoria constante para archivos grandes
JavaScript@json2csvParserCampos custom, transforms, unwind
JavaScriptManualObject.keys() + join()Cero dependencias, frágil con datos complejos
JavaJackson + commons-csvIteración manualRobusto, con boilerplate verboso
Javaunivocity-parsersCsvWriterAlternativa de alto rendimiento

Lo que Funciona

  • Deriva las cabeceras de la unión de claves de todos los registros, no solo del primero — las claves ausentes producen columnas desalineadas o truncadas.
  • Sanitiza las cabeceras: elimina espacios y caracteres especiales que rompen parsers posteriores.
  • Escapa comas y comillas en los valores de texto para producir CSV conforme a RFC 4180; las librerías dedicadas lo hacen automáticamente.
  • Decide la estrategia para arrays desde el inicio: desenróllalos en filas repetidas o guárdalos como strings JSON dentro de las celdas.
  • Procesa datasets grandes en streaming: para archivos mayores que la memoria disponible, usa ijson en Python o un transform stream en Node — consulta Leer archivos grandes en streaming en Node.js.
  • Conserva los valores en crudo: no conviertas números o booleanos a texto antes de escribir, o Excel pierde la capacidad de ordenarlos y calcularlos.

Errores Comunes

  • Asumir que todos los registros tienen claves idénticas: los campos faltantes desalinean columnas; calcula primero la unión de claves.
  • No manejar objetos anidados: produce [object Object] en JS o LinkedHashMap en Java dentro de las celdas, en lugar de valores reales.
  • Olvidar entrecomillar valores con comas: rompe parsers que separan por comas — RFC 4180 exige entrecomillado.
  • Ignorar la inyección de fórmulas CSV: las celdas que empiezan por =, +, - o @ se ejecutan como fórmulas al abrirlas en Excel. Préfixalas con ' o sanitízalas al exportar, sobre todo con datos generados por usuarios (ver OWASP CSV Injection).
  • Cargar archivos de varios GB en memoria: un array JSON grande convertido en memoria tumba el proceso; usa streaming.
  • Confiar en el delimitador por defecto de Excel: muchas configuraciones regionales esperan punto y coma — define el delimitador explícitamente para usuarios internacionales.

Cuándo No Usar Este Enfoque

  • JSON irregular o profundamente recursivo: los documentos con estructuras muy distintas pierden información en una tabla plana — conserva JSON Lines o convierte a Parquet.
  • Intercambio máquina a máquina: CSV pierde tipos y anidación; usa JSON, Parquet o Arrow para datos entre servicios.
  • Datos que ya están en una base de datos: exporta CSV directamente con SQL (COPY, SELECT INTO OUTFILE) en lugar de pasar por JSON.
  • Documentos con varias entidades: cuando un JSON contiene varias entidades relacionadas, varios CSV vinculados o un formato relacional funcionan mejor que una tabla gigante aplanada.

Ver También

Preguntas frecuentes

¿Cómo convierto JSON profundamente anidado a CSV?

Usa pandas.json_normalize con sep='_' o la opción unwind de @json2csv para arrays. Para objetos muy anidados, considera si CSV es el formato adecuado — Parquet o JSON Lines pueden ser mejores alternativas. Si necesitas CSV sí o sí, aplana las claves en columnas con notación de punto.

¿Puedo convertir JSON a CSV en el navegador?

Sí. Carga @json2csv vía CDN o inclúyelo en el bundle de tu aplicación frontend. Para archivos muy grandes, usa Web Workers para no bloquear el hilo principal y envía chunks a la descarga con la Streams API o Blob/URL.createObjectURL.

¿Cómo manejo arrays dentro de objetos JSON al convertir a CSV?

Opción 1: Desenrolla el array para que cada elemento sea una fila separada (duplicando los campos del padre). Opción 2: Serializa el array como string JSON dentro de la celda CSV. Opción 3: Crea un CSV relacionado aparte y usa una columna ID para vincularlos, como en la normalización de bases de datos.

¿CSV preserva los tipos de datos de JSON?

No — cada celda es texto. Los números siguen siendo ordenables porque Excel los vuelve a inferir, pero los booleanos quedan como cadenas true/false, null se convierte en celda vacía o en el literal "null" según la librería, y las estructuras anidadas requieren aplanado o serialización. Si necesitas preservar tipos, usa Parquet o conserva los datos como JSON.

¿Por qué mi CSV se ve roto en Excel?

Dos causas frecuentes: codificación y delimitador. Excel necesita un BOM UTF-8 (\ufeff) para detectar Unicode correctamente, y en muchas configuraciones regionales espera ; en lugar de , como separador. Escribe el BOM y define el delimitador que espera tu audiencia, o comparte un .xlsx en su lugar.