StackPractices
beginner Por Mathias Paulenko

Convertir CSV a JSON

Convertí archivos CSV a JSON estructurado con Python, JavaScript y Java. Elegí la librería adecuada para conversiones puntuales o pipelines grandes.

Temas: data

Visión General

CSV es el formato de exportación por defecto de spreadsheets y bases de datos, pero solo guarda texto plano. JSON te da números, booleanos, objetos anidados y arrays — la forma que la mayoría de las APIs y document stores esperan.

Dejé ejemplos más abajo para Python, JavaScript y Java. Elegí el que se ajuste a tu stack. Yo uso la versión rápida con librería estándar para archivos chicos, pandas cuando necesito inferencia de tipos, y un parser de streaming para lo que no entra en memoria. Si querés ir en el otro sentido, mirá Convertir JSON a CSV y Serializar y Deserializar Datos para los conceptos de estructura de datos alrededor.

Cuándo Usar

Uso esta receta cuando necesito:

  • llevar exports de spreadsheets a una app web o API.
  • llevar datos planos a MongoDB, Elasticsearch u otro document store.
  • alimentar una librería de gráficos del lado del cliente con datos CSV.
  • procesar un CSV demasiado grande para caber en memoria como un solo objeto, una fila a la vez.

Elegir un enfoque

Diagrama de flujo para elegir un enfoque CSV a JSON según tamaño y entorno

Cuándo No Usar

  • Si los datos ya viven en una base de datos, consultala directamente con SQL en lugar de exportar a CSV y parsearlo de nuevo.
  • El archivo es Parquet, Avro u ORC; usá la herramienta hecha para ese formato.
  • Estás procesando un flujo en vivo de registros. Ahí la conversión por lotes de archivos no tiene sentido; usá un pipeline de streaming.
  • El CSV contiene estructuras profundamente anidadas que se expresan más fácil en XML o JSON desde el origen.

Solución

Python (librería estándar)

# csv + json de la librería estándar
import csv
import json

with open('data.csv', newline='', encoding='utf-8') as f:
    reader = csv.DictReader(f)
    rows = list(reader)

with open('data.json', 'w', encoding='utf-8') as f:
    json.dump(rows, f, indent=2)

Python (pandas)

# pip install pandas
import pandas as pd

df = pd.read_csv('data.csv')
df['date'] = pd.to_datetime(df['date'])
json_data = df.to_json(orient='records', date_format='iso')
print(json_data)

JavaScript (Node con csv-parse)

// npm install csv-parse
import { parse } from 'csv-parse';
import fs from 'fs';

const parser = fs.createReadStream('data.csv').pipe(
  parse({ columns: true, cast: true })
);

const rows = [];
for await (const row of parser) {
  rows.push(row);
}
console.log(JSON.stringify(rows, null, 2));

JavaScript (browser con PapaParse)

// npm install papaparse
import Papa from 'papaparse';

const csv = 'name,age\nAlice,30\nBob,25';
const result = Papa.parse(csv, { header: true });
console.log(JSON.stringify(result.data, null, 2));

Java (Jackson)

// Maven: com.fasterxml.jackson.dataformat:jackson-dataformat-csv
import java.io.File;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;

public class CsvToJson {
    public static void main(String[] args) throws Exception {
        CsvSchema schema = CsvSchema.builder()
            .setUseHeader(true)
            .build();
        CsvMapper csvMapper = new CsvMapper();
        ObjectMapper jsonMapper = new ObjectMapper();

        List<Map<String, String>> rows = csvMapper
            .readerFor(Map.class)
            .with(schema)
            .readValues(new File("data.csv"))
            .readAll();

        jsonMapper.writerWithDefaultPrettyPrinter()
            .writeValue(new File("data.json"), rows);
    }
}

Java (Apache Commons CSV)

// Maven: org.apache.commons:commons-csv:1.11.0
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;

public class CsvToJsonCommons {
    public static void main(String[] args) throws Exception {
        List<Map<String, String>> rows = new ArrayList<>();
        try (FileReader fr = new FileReader("data.csv")) {
            Iterable<CSVRecord> records = CSVFormat.DEFAULT
                .withFirstRecordAsHeader()
                .parse(fr);
            for (CSVRecord record : records) {
                Map<String, String> row = new LinkedHashMap<>();
                record.toMap().forEach(row::put);
                rows.add(row);
            }
        }
        new ObjectMapper()
            .writerWithDefaultPrettyPrinter()
            .writeValue(new File("data.json"), rows);
    }
}

Explicación

CSV solo guarda texto, así que no distingue números, booleanos ni fechas. JSON puede almacenar números, booleanos, null, arrays y objetos — vos decidís cómo mapea cada valor a un tipo: age va a número, active a booleano, tags a array. csv.DictReader de Python y csv-parse de Node devuelven strings por defecto, así que casteás manualmente o definís un schema.

Un parser de streaming lee una fila a la vez, así que el archivo nunca tiene que quedarse en memoria. Si necesitás JSON anidado, usá nombres de columna con puntos como user.name y una utilidad para aplanar, o armá el objeto en el código. Para ver más a fondo ese patrón, mirá Aplanar y Desanidar Objetos.

Las comillas y los escapes son donde line.split(',') se rompe. Una coma entre comillas o un salto de línea dentro de una celda son válidos en CSV, y una comilla dentro de una celda se escapa con otra comilla. Por eso un parser real gana siempre contra una regex o un split. Cuando exporto desde Excel, también me fijo el BOM UTF-8 al principio del archivo, que puede corromper el primer header y hacer que DictReader genere una clave como \ufeffid.

Variantes

TecnologíaLibreríaEnfoqueNotas
Pythoncsv + jsonDictReader + json.dumpLibrería estándar, sin dependencias
Pythonpandasread_csv + to_jsonInferencia de tipos, maneja fechas, archivos grandes
JavaScriptcsv-parseparse({ columns: true })Streaming, async iterables, enfocado en Node
JavaScriptpapaparsePapa.parse(csv, { header: true })Browser + Node, tolera CSV malformado
JavaJackson CSVCsvMapper + ObjectMapperStreaming, schema-driven
JavaApache Commons CSVCSVFormat.DEFAULT.parse()Ligero, serialización JSON manual

Yo uso csv.DictReader o Papa.parse cuando quiero código sin instalar nada. pandas es mi primera opción cuando el CSV tiene fechas, tipos mezclados o necesito una vista previa. Paso a csv-parse con async iteration o CsvMapper con readValues cuando el archivo es demasiado grande para la memoria o cuando quiero que las filas fluyan directo a otro consumidor asíncrono.

Herramientas y Ecosistema

La tabla de abajo tiene las librerías que me cruzo más seguido en producción. Las versiones cambian, así que las fijo en el companion repo o en un requirements.txt/package.json.

Herramienta / LibreríaRolReferencia
csv de PythonLector/escritor de la librería estándardocs.python.org/library/csv
pandasConversión de DataFrame e inferencia de tipospandas.pydata.org/docs/reference/api/pandas.read_csv.html
csv-parseParser de streaming para Nodecsv.js.org
PapaParseParser rápido para browser y Nodewww.papaparse.com
Jackson CSVParser de streaming en Java, schema-drivengithub.com/FasterXML/jackson-dataformat-csv
Apache Commons CSVParser ligero en Javacommons.apache.org/proper/commons-csv/
RFC 4180Especificación del formato CSVdatatracker.ietf.org/doc/html/rfc4180

Si necesitás validar la forma de salida contra un contrato, combiná esta receta con Validar JSON Schema después de la conversión.

Mejores Prácticas

  • Mapeá los headers CSV a claves JSON con columns: true o DictReader; no confíes en las posiciones, porque pueden cambiar entre exports.
  • Acordate de castear los tipos de forma explícita. CSV no tiene booleanos ni fechas, así que definí un schema o post-procesá las filas. Suelo tener un TYPE_MAP chico para cada columna.
  • Hacé streaming una vez que el archivo pasa los 100 MB. Escribí el JSON en pedazos, o cargalo directamente a una base de datos. Cargar un archivo de varios gigabytes en una lista casi siempre termina en out-of-memory.
  • Validá el JSON de salida contra un schema cuando la estructura importe.
  • Mantené el encoding UTF-8 explícito y manejá los BOM, especialmente con exports de Excel. Abrí archivos con encoding='utf-8-sig' cuando un BOM es probable.

Errores Comunes

  • Subir un CSV de varios gigabytes entero a memoria. Eso suele terminar con un ticket para reescribir el script.
  • Separar filas con line.split(',') y romper con comillas o saltos de línea. Un CSV real puede tener saltos de línea dentro de celdas entre comillas.
  • Referenciar columnas por índice cuando los headers pueden cambiar; la primera columna de hoy puede no ser la primera de mañana.
  • Ignorar un BOM UTF-8 que corrompe la primera clave del header. Si ves  en tus claves, ahí está el BOM.
  • Olvidarse de que JSON no tiene tipo date. Usá strings en formato ISO 8601 y dejá que el consumidor los vuelva a convertir después.

Ver También

Preguntas frecuentes

¿Por qué todos los valores salen como strings?

CSV no almacena tipos. Usá un schema o funciones de casteo para convertir números, booleanos y fechas a los tipos JSON correctos.

¿Puedo convertir un CSV sin cargarlo entero en memoria?

Sí. Usá csv-parse con async iteration en Node, la API de streaming de Jackson en Java, o pd.read_csv(chunksize=...) en Python.

¿Cómo creo objetos JSON anidados desde columnas planas de CSV?

Usá nombres de columna con puntos como user.name y user.email, luego expandilos en el código o con una librería como flat o pandas.json_normalize.

¿Qué hago si el CSV tiene un delimitador distinto?

Seteá el delimiter del parser para que coincida con el archivo. csv.Sniffer en Python, la opción delimiter de csv-parse y el export "CSV (punto y coma)" de Excel son soluciones comunes. RFC 4180 usa coma por defecto, pero archivos delimitados por tabulación o punto y coma siguen siendo válidos.

¿Cómo manejo CSV malformado?

Usá un parser que no se rompa fácil, como papaparse con skipEmptyLines y callbacks de error, o configurá csv-parse para saltar líneas vacías y seguir con los registros buenos.

¿Puedo convertir CSV a JSON dentro del browser?

Sí. PapaParse corre en el browser con un string o un input File. Podés parsear un archivo que el usuario arrastra a la página y luego llamar JSON.stringify(result.data).

¿Qué es un BOM UTF-8 y por qué rompe mis headers?

Un BOM son algunos bytes extra (\ufeff) que algunos editores y Excel agregan al principio de un archivo UTF-8. Ese prefijo termina mezclado con el nombre de la primera columna. Abrí el archivo con encoding='utf-8-sig' en Python o sacá el BOM antes de parsear.

¿Cómo preservo fechas y booleanos en la salida JSON?

CSV no tiene tipo date ni boolean, así que los tenés que castear. Convierto las fechas a strings ISO 8601 y los booleanos desde strings como "true" o "false" antes del json.dump o JSON.stringify final.