Convertir Excel a JSON es una tarea habitual en integración de datos: alimentar APIs, configurar aplicaciones o transformar reportes en formatos consumibles por JavaScript. Python con pandas hace este proceso rápido y flexible.
Instalación
pip install pandas openpyxl
# openpyxl es necesario para leer archivos .xlsx
Conversión básica de hoja a JSON
import pandas as pd
import json
# Leer Excel y convertir a JSON
df = pd.read_excel('datos.xlsx', sheet_name='Hoja1')
# orient='records' → lista de objetos [{col: val, ...}, ...]
json_str = df.to_json(orient='records', force_ascii=False, indent=2)
print(json_str[:300])
# Guardar a archivo
with open('datos.json', 'w', encoding='utf-8') as f:
f.write(json_str)
# Alternativa: via dict → json
registros = df.to_dict(orient='records')
with open('datos.json', 'w', encoding='utf-8') as f:
json.dump(registros, f, ensure_ascii=False, indent=2)
print(f"Convertidos: {len(registros)} registros")
Opciones de orient en to_json()
import pandas as pd
import json
df = pd.DataFrame({
'id': [1, 2, 3],
'nombre': ['Ana', 'Luis', 'María'],
'puntuacion': [95.5, 87.0, 92.3],
})
# records — lista de objetos (el más común para APIs)
print(df.to_json(orient='records', indent=2))
# [{"id":1,"nombre":"Ana","puntuacion":95.5}, ...]
# index — dict con índices como claves
print(df.to_json(orient='index', indent=2))
# {"0":{"id":1,...}, "1":{"id":2,...}}
# columns — dict de columnas (útil para series de tiempo)
print(df.to_json(orient='columns', indent=2))
# {"id":{"0":1,"1":2},"nombre":{"0":"Ana",...}}
# values — solo los valores como array 2D
print(df.to_json(orient='values'))
# [[1,"Ana",95.5],[2,"Luis",87.0],...]
# split — separar índice, columnas y datos
print(df.to_json(orient='split', indent=2))
# {"columns":["id","nombre","puntuacion"],"data":[[1,"Ana",95.5],...]}
Múltiples hojas → múltiples objetos JSON
import pandas as pd
import json
# Leer todas las hojas
hojas = pd.read_excel('informe.xlsx', sheet_name=None) # None = todas las hojas
print(f"Hojas encontradas: {list(hojas.keys())}")
# Opción 1: un JSON por hoja
for nombre_hoja, df in hojas.items():
registros = df.to_dict(orient='records')
nombre_archivo = f"{nombre_hoja.replace(' ', '_').lower()}.json"
with open(nombre_archivo, 'w', encoding='utf-8') as f:
json.dump(registros, f, ensure_ascii=False, indent=2)
print(f" {nombre_hoja}: {len(registros)} registros → {nombre_archivo}")
# Opción 2: todas las hojas en un JSON con claves por nombre de hoja
resultado = {}
for nombre_hoja, df in hojas.items():
resultado[nombre_hoja] = df.to_dict(orient='records')
with open('informe_completo.json', 'w', encoding='utf-8') as f:
json.dump(resultado, f, ensure_ascii=False, indent=2)
print(f"\nJSON combinado: informe_completo.json")
Limpiar datos antes de exportar
import pandas as pd
import json
import numpy as np
df = pd.read_excel('datos_sucios.xlsx')
# 1. Eliminar filas completamente vacías
df = df.dropna(how='all')
# 2. Limpiar nombres de columnas (sin espacios, minúsculas)
df.columns = (df.columns
.str.strip()
.str.lower()
.str.replace(' ', '_')
.str.replace('[^a-z0-9_]', '', regex=True))
# 3. Rellenar NaN con None (JSON null)
df = df.where(pd.notna(df), None)
# 4. Convertir tipos de datos
if 'fecha' in df.columns:
df['fecha'] = pd.to_datetime(df['fecha']).dt.strftime('%Y-%m-%d')
if 'precio' in df.columns:
df['precio'] = df['precio'].round(2)
# 5. Exportar limpio
registros = df.to_dict(orient='records')
# Eliminar valores None de cada registro (opcional)
registros_limpios = [
{k: v for k, v in r.items() if v is not None}
for r in registros
]
with open('datos_limpios.json', 'w', encoding='utf-8') as f:
json.dump(registros_limpios, f, ensure_ascii=False, indent=2)
print(f"Exportados: {len(registros_limpios)} registros")
JSON anidado (relación padre-hijos)
import pandas as pd
import json
from itertools import groupby
# Excel con datos relacionales:
# id_cliente | nombre_cliente | id_pedido | producto | cantidad | precio
df = pd.read_excel('clientes_pedidos.xlsx')
# Crear JSON anidado: cada cliente con su lista de pedidos
clientes_json = []
for id_cliente, grupo in df.groupby('id_cliente'):
cliente = {
'id': int(id_cliente),
'nombre': grupo['nombre_cliente'].iloc[0],
'pedidos': [
{
'id_pedido': int(row['id_pedido']),
'producto': row['producto'],
'cantidad': int(row['cantidad']),
'precio': float(row['precio']),
}
for _, row in grupo.iterrows()
],
'total_pedidos': len(grupo),
'gasto_total': float(grupo['precio'].sum()),
}
clientes_json.append(cliente)
with open('clientes_con_pedidos.json', 'w', encoding='utf-8') as f:
json.dump(clientes_json, f, ensure_ascii=False, indent=2)
print(f"Clientes exportados: {len(clientes_json)}")
Conversión con filtros y transformaciones
import pandas as pd
import json
df = pd.read_excel('productos.xlsx')
# Filtrar, transformar y exportar
resultado = (
df
# Filtrar activos con precio > 0
.query("activo == True and precio > 0")
# Seleccionar columnas
[['id', 'nombre', 'categoria', 'precio', 'stock']]
# Agregar campo calculado
.assign(
precio_con_iva=lambda x: (x['precio'] * 1.21).round(2),
disponible=lambda x: x['stock'] > 0,
)
# Renombrar columnas para la API
.rename(columns={
'nombre': 'name',
'categoria': 'category',
'precio': 'price',
'stock': 'quantity',
})
# Convertir a dict
.to_dict(orient='records')
)
with open('productos_api.json', 'w', encoding='utf-8') as f:
json.dump(resultado, f, ensure_ascii=False, indent=2)
print(f"Productos exportados: {len(resultado)}")
Batch: convertir múltiples Excel a JSON
import pandas as pd
import json
from pathlib import Path
def excel_dir_a_json(directorio_entrada, directorio_salida=None):
origen = Path(directorio_entrada)
destino = Path(directorio_salida) if directorio_salida else origen
destino.mkdir(parents=True, exist_ok=True)
archivos = list(origen.glob('*.xlsx')) + list(origen.glob('*.xls'))
print(f"Encontrados: {len(archivos)} archivos Excel")
for archivo in sorted(archivos):
try:
hojas = pd.read_excel(archivo, sheet_name=None)
if len(hojas) == 1:
# Una sola hoja: JSON plano
nombre_hoja = list(hojas.keys())[0]
df = hojas[nombre_hoja].dropna(how='all')
datos = df.to_dict(orient='records')
else:
# Múltiples hojas: objeto con claves por hoja
datos = {
k: v.dropna(how='all').to_dict(orient='records')
for k, v in hojas.items()
}
json_path = destino / archivo.with_suffix('.json').name
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(datos, f, ensure_ascii=False, indent=2, default=str)
print(f" OK: {archivo.name} → {json_path.name}")
except Exception as e:
print(f" ERROR: {archivo.name}: {e}")
excel_dir_a_json('reportes/', 'json_output/')
Conversiones relacionadas
Conversiones de documento que siguen este tema: