JSON a Excel: cuatro enfoques y dónde pierde fidelidad cada uno
En resumen: Las estructuras anidadas, los campos inconsistentes y los arrays de arrays son donde se rompe la conversión de JSON a hoja de cálculo. Cuatro enfoques ordenados por tiempo hasta el resultado, cada uno con sus compromisos.

En resumen: un array plano de objetos o un objeto que envuelve un array se convierten más rápido con la herramienta en línea; usa Power Query cuando los datos se refresquen semanalmente, pandas cuando haya muchos campos que limpiar y un script Node solo cuando la conversión deba integrarse en una tubería, pero decide qué significa "una fila" antes de tocar datos anidados o claves inconsistentes.
La conversión en sí no es difícil; lo difícil es descubrir después que los números se volvieron texto y que varios niveles de anidamiento colapsaron en uno. Lee la estructura de los datos antes de elegir un método y te ahorras un retrabajo.
1. Primero, averigua qué tipo de JSON tienes
Abre el archivo y mira el nivel más externo:
| Estructura | Forma | Dificultad de conversión |
|---|---|---|
| Array plano de objetos | [{"a":1,"b":2},{...}] |
La más sencilla: una fila por registro |
| Objeto que envuelve un array | {"data":[{...}],"total":100} |
Fácil: toma el nivel data |
| Objetos anidados | {"user":{"name":"x"}} |
Media: decide si aplanar o unir en una cadena |
| Arrays de arrays | {"tags":["a","b"]} |
Dolorosa: no cabe en una fila, requiere dividir en tablas |
| Claves inconsistentes | Cada registro tiene campos distintos | Dolorosa: las columnas acaban casi vacías |
Las dos primeras se convierten directamente con una tasa de éxito cercana al 100%; para las tres últimas, decide primero qué significa "una fila", de lo contrario la hoja resultante es inutilizable.
2. Enfoque 1: herramienta en línea (1 minuto)
Abre JSON a tabla, pega los datos → elige el delimitador y la cabecera → exporta CSV → ábrelo en Excel. Adecuado para entre unas decenas y unos miles de filas de datos bien estructurados.
Tres cosas a las que debes prestar atención:
- Obtienes CSV, no xlsx. Cuando Excel abre un CSV, adivina la codificación a partir de la configuración regional del sistema, así que el texto chino puede volverse mojibake. Usar "Datos → Desde texto/CSV" y seleccionar UTF-8 explícitamente es más fiable.
- Los identificadores que empiezan por
0pierden sus ceros."007"es texto en el CSV, pero Excel lo convierte automáticamente en el número 7. Configura esa columna como texto durante la importación, o añádele un prefijo en JSON. - Los números largos se convierten en notación científica. Los documentos nacionales de identidad y los números de pedido de más de 15 dígitos pierden precisión; esos campos ya deberían ser cadenas en JSON y deben seguir en formato de texto tras la importación.
3. Enfoque 2: Power Query integrado en Excel (reutilizable)
Cuando tus datos se refrescan cada semana, usa el Power Query integrado en Excel: configúralo una vez y después solo hay que pulsar "Actualizar":
- "Datos" → "Obtener datos" → "Desde archivo" → "Desde JSON";
- Después de seleccionar el archivo llegas al editor de Power Query, donde ves estructuras
List/Record; - Pulsa el icono de expansión a la derecha de la cabecera de una columna para expandir los campos anidados nivel a nivel;
- Cuando los nombres de columna y los tipos te parezcan correctos, "Cerrar y cargar".
Lo bueno es que el anidamiento se puede expandir en varias columnas capa por capa y los tipos se pueden asignar manualmente; el precio es que la primera configuración exige entender la diferencia entre List y Record: List es un array, Record es un objeto, y se expanden en direcciones distintas.
4. Enfoque 3: Python + pandas (muchos campos, necesita limpieza)
import pandas as pd
# Objeto que envuelve un array: primero llega al nivel de la lista
rows = pd.read_json('data.json')['data']
# Expande los campos anidados en varias columnas
frame = pd.json_normalize(rows, sep='_')
frame.to_excel('out.xlsx', index=False)
El parámetro clave de json_normalize es sep, que determina el estilo de nombrado de columnas tras expandir los campos anidados; user_name es más fácil de usar en Excel que user.name. Los arrays de arrays (como tags) necesitan un paso extra de explode:
frame = frame.explode('tags')
Este paso divide una fila en varias, la única forma de preservar una relación uno a muchos, a costa de duplicar los campos restantes.
5. Enfoque 4: un script Node (cuando debe integrarse en una tubería)
import { readFileSync, writeFileSync } from 'node:fs';
const rows = JSON.parse(readFileSync('data.json', 'utf8'));
const headers = [...new Set(rows.flatMap((row) => Object.keys(row)))];
const escapeCell = (value) => `"${String(value ?? '').replaceAll('"', '""')}"`;
const lines = [
headers.join(','),
...rows.map((row) => headers.map((key) => escapeCell(row[key])).join(',')),
];
writeFileSync('out.csv', '\uFEFF' + lines.join('\n'), 'utf8');
Dos detalles que no debes saltarte: escribir \uFEFF (un BOM) al principio para que Excel reconozca UTF-8, y escapar las comillas dobles dentro de las celdas como "", de lo contrario las columnas se desplazan.
6. Tres cosas que la conversión siempre pierde
| Elemento perdido | Síntoma | Remedio |
|---|---|---|
| Información de tipo | Los números y booleanos se vuelven texto o TRUE/FALSE |
Configura los tipos de columna manualmente tras la importación, o especifícalos en Power Query |
| Niveles de anidamiento | Los campos profundos colapsan en [object Object] |
Usa json_normalize o expande nivel a nivel; nunca String(obj) |
| Relaciones uno a muchos | Los arrays se meten en una única celda | Divide las filas con explode, o crea una tabla relacionada aparte |
Además, null y "campo ausente" se ven igual en una hoja (ambos son celdas vacías) pero significan cosas distintas en el dominio de negocio. Cuando necesites distinguirlos, convierte explícitamente null en la cadena "null" en JSON, o añade una columna marcadora _has_field.
7. Verificar después: no mires solo la primera fila
Tras la conversión, comprueba al menos tres cosas:
- Recuento de registros: el número de líneas del CSV debe ser igual a la longitud del array + 1 (la cabecera); una línea que falta normalmente significa que se tragó el salto de línea final;
- La columna con mayor tasa de nulos: muchas celdas vacías indican nombres de clave inconsistentes; vuelve atrás y comprueba si el JSON original usa dos grafías (
userIdyuser_id); - La fila más larga: los arrays anidados suelen aterrizar ahí; confirma que no se truncó nada.
Cuando necesites la operación inversa (Excel/CSV de vuelta a JSON), usa CSV a JSON, y recuerda que la cabecera se trata como los nombres de clave: cambiar la cabecera es cambiar los nombres de campo.
Preguntas frecuentes
¿Cómo elijo entre la herramienta en línea, Power Query y pandas?
Usa la herramienta en línea para entre unas decenas y unos miles de filas de datos bien estructurados que conviertas una sola vez. Usa Power Query cuando los datos se refresquen cada semana, ya que se configura una vez y se actualiza con un solo clic. Usa pandas con json_normalize cuando haya muchos campos que limpiar, y un script Node cuando la conversión deba integrarse en una tubería.
¿Por qué el texto chino se convierte en mojibake en un CSV?
La exportación es CSV, no xlsx, y Excel adivina la codificación a partir de la configuración regional del sistema, así que UTF-8 sin BOM muestra mojibake. Escribe un BOM \uFEFF al principio del archivo, o ábrelo mediante "Datos → Desde texto/CSV" y selecciona UTF-8 explícitamente.
¿Los objetos anidados deben aplanarse o dividirse en filas adicionales?
Aplánalos nivel a nivel en lugar de pegarlos como cadenas: Power Query expande los campos anidados en columnas, y json_normalize con sep hace lo mismo en pandas. Los arrays de arrays necesitan un paso extra de explode que divide una fila en varias. Pasar un objeto profundo a String(obj) solo produce [object Object].