Serialización JSON y Metadatos en Python
🗺️ Mapa de la Lección
⏱ 1 hora🟡 Intermedio🎯 Qué aprenderás
- Leer y escribir archivos JSON
- Convertir entre JSON y estructuras de Python
- Trabajar con JSON anidado complejo
- Manejar errores de parsing JSON
- Formatear JSON para legibilidad
🌍 Para qué te sirve
- APIs REST: recibir y enviar datos JSON
- Archivos de configuración (package.json, config.json)
- Comunicación entre servicios
- Almacenamiento de datos estructurados
- Integración con servicios web modernos
- Exportar/importar datos de aplicaciones
🎯 ¿Por qué aprender JSON?
JSON es el formato de datos más usado en la web moderna. Si trabajas con APIs, servicios web, o configuraciones, te encontrarás con JSON constantemente.
JSON es esencial porque:
- Estándar de APIs: REST APIs, GraphQL usan JSON
- Universal: casi todos los lenguajes lo soportan
- Legible: fácil de leer y escribir por humanos
- Ligero: menos verboso que XML
- Moderno: es el formato preferido en desarrollo web actual
🌍 Casos reales donde se usa
JSON está en prácticamente todos los sistemas modernos:
- APIs REST: Recibir y enviar datos JSON
- Archivos de configuración: package.json, config.json
- Comunicación entre servicios: Microservicios intercambian JSON
- Almacenamiento de datos: Bases de datos NoSQL como MongoDB
- Integración con servicios web: Todos los servicios modernos usan JSON
- Exportar/importar datos: De aplicaciones y sistemas
Ejemplo real: Cuando usas una API como Twitter o GitHub, todas las respuestas vienen en formato JSON. Es el lenguaje universal de la web moderna.
💡 Concepto base
JSON (JavaScript Object Notation) es un formato de datos ligero y legible que se parece mucho a los diccionarios de Python. Es el formato preferido para comunicarse entre sistemas.
Lo genial de Python: El módulo json está incluido en la librería estándar y es muy fácil de usar.
import json
# Diccionario de Python
receta = {
"tortillas": 10,
"salsa": "roja",
"queso": "fresco",
"pastor": True
}
# Convertir a JSON
json_string = json.dumps(receta)
print(json_string)
# Convertir de JSON a Python
receta_recuperada = json.loads(json_string)
print(receta_recuperada["salsa"])
{"tortillas": 10, "salsa": "roja", "queso": "fresco", "pastor": true}
roja
JSON es como una receta moderna, simple y directa: {"tortillas": 10, "salsa": "roja", "queso": "fresco", "pastor": true}. Es fácil de leer, escribir y entender, como una lista de ingredientes clara. Es el formato preferido para comunicarse entre sistemas (como compartir recetas entre restaurantes), porque es ligero, legible y universal. Todos los sistemas modernos "hablan" JSON, igual que todos los chefs modernos entienden recetas en formato JSON.
JSON se parece mucho a los diccionarios de Python. La diferencia principal es que JSON usa comillas dobles y algunos tipos de datos son ligeramente diferentes. Python puede convertir fácilmente entre JSON y diccionarios.
- Archivos de configuración (package.json, tsconfig.json, etc.)
- Comunicación entre servicios
- Almacenamiento de datos estructurados
Ventaja para Python: JSON se mapea naturalmente a diccionarios y listas de Python. Es casi como trabajar con estructuras nativas.
Antes de continuar: Asegúrate de entender Pathlib, Diccionarios y Sets y Funciones.
¿Qué es JSON?
JSON es un formato de datos basado en texto que representa objetos de JavaScript. Es ampliamente utilizado para:
- APIs web (REST, GraphQL)
- Configuración de aplicaciones
- Almacenamiento de datos
- Intercambio de datos entre sistemas
Estructura Básica de JSON
{
"datos": [
{
"ip": "192.168.1.1",
"ruta_script": "/home/user/script.sh"
},
{
"ip": "10.0.0.1",
"ruta_script": "/opt/scripts/test.sh"
}
],
"total_registros": 2,
"tipo": "validos",
"metadatos": {
"fecha_exportacion": "2024-01-15 14:30:25",
"columnas": ["ip", "ruta_script"],
"configuracion": {
"indentacion": 2,
"formato_fecha": "%Y-%m-%d %H:%M:%S"
}
}
}
Tipos de datos soportados:
- Objetos:
{"clave": "valor"} - Arrays:
["item1", "item2"] - Strings:
"texto" - Números:
123,3.14 - Booleanos:
true,false - Null:
null
Módulo json de Python
Python incluye el módulo json en la biblioteca estándar (no necesitas instalar nada):
import json
Funciones Principales
El módulo json tiene 4 funciones principales. La diferencia entre dumps/loads y dump/load es:
dumps/loads(con 's' de string): Trabajan con strings en memoriadump/load(sin 's'): Trabajan directamente con archivos
Serialización (Python → JSON)
Convertir datos de Python a formato JSON:
# json.dumps() - Python a string JSON
datos_python = {"nombre": "Ana", "edad": 25}
json_string = json.dumps(datos_python)
print(json_string) # '{"nombre": "Ana", "edad": 25}'
# json.dump() - Python a archivo JSON
with open('datos.json', 'w') as f:
json.dump(datos_python, f) # Escribe directamente al archivo
¿Cuándo usar cada una?
dumps(): Cuando necesitas el JSON como string (para enviar por API, guardar en variable, etc.)dump(): Cuando quieres escribir directamente a un archivo (más eficiente)
Deserialización (JSON → Python)
Convertir JSON a datos de Python:
# json.loads() - String JSON a Python
json_string = '{"nombre": "Ana", "edad": 25}'
datos_python = json.loads(json_string)
print(datos_python) # {'nombre': 'Ana', 'edad': 25}
print(type(datos_python)) # <class 'dict'>
# json.load() - Archivo JSON a Python
with open('datos.json', 'r') as f:
datos_python = json.load(f) # Lee directamente del archivo
Mapeo automático:
- JSON object → Python dict
- JSON array → Python list
- JSON string → Python str
- JSON number → Python int o float
- JSON boolean → Python bool
- JSON null → Python None
Conversión de DataFrame a JSON
Método Básico
import pandas as pd
import json
# DataFrame de ejemplo
df = pd.DataFrame({
'ip': ['192.168.1.1', '10.0.0.1'],
'ruta_script': ['/home/user/script.sh', '/opt/scripts/test.sh']
})
# Conversión simple
json_string = df.to_json(orient='records', indent=2)
print(json_string)
Método Avanzado con Metadatos
def dataframe_a_json(df, config, tipo_datos="validos"):
"""Convierte DataFrame a JSON con metadatos."""
from datetime import datetime
# Convertir DataFrame a lista de diccionarios
datos = df.to_dict('records')
# Crear estructura base
estructura = {
"datos": datos,
"total_registros": len(datos),
"tipo": tipo_datos
}
# Agregar metadatos si está habilitado
if config.get('incluir_metadatos', True):
estructura["metadatos"] = {
"fecha_exportacion": datetime.now().strftime(
config.get('formato_fecha', '%Y-%m-%d %H:%M:%S')
),
"columnas": list(df.columns),
"configuracion": {
"indentacion": config.get('indentacion', 2),
"formato_fecha": config.get('formato_fecha', '%Y-%m-%d %H:%M:%S')
}
}
return estructura
Configuración de Exportación JSON
Parámetros de json.dump()
def exportar_a_json(df, archivo_salida, config, tipo_datos="validos"):
"""Exporta DataFrame a JSON con configuración."""
try:
if df.empty:
print(f"No hay datos para exportar a {archivo_salida}")
return
# Crear estructura JSON
estructura = dataframe_a_json(df, config, tipo_datos)
# Configurar indentación
indentacion = config.get('indentacion', 2)
# Escribir archivo
with open(archivo_salida, 'w', encoding='utf-8') as archivo:
json.dump(
estructura,
archivo,
indent=indentacion,
ensure_ascii=False, # Soporte para caracteres especiales
separators=(',', ': ') # Formato compacto
)
print(f"Datos exportados a JSON: {archivo_salida}")
except Exception as e:
print(f"Error al exportar a JSON: {e}")
raise
Parámetros Importantes
- indent: Número de espacios para indentación
- ensure_ascii: Si es
False, permite caracteres Unicode - separators: Tupla con separadores personalizados
- sort_keys: Si es
True, ordena las claves alfabéticamente
Tipos de Metadatos
Metadatos de Exportación
metadatos_exportacion = {
"fecha_exportacion": "2024-01-15 14:30:25",
"usuario": "admin",
"version_aplicacion": "1.0.0",
"formato_datos": "JSON"
}
Metadatos de Datos
metadatos_datos = {
"total_registros": 1000,
"columnas": ["ip", "ruta_script"],
"tipos_datos": {
"ip": "string",
"ruta_script": "string"
},
"filtros_aplicados": ["ip_válida", "ruta_válida"]
}
Metadatos de Configuración
metadatos_configuracion = {
"configuracion": {
"indentacion": 2,
"formato_fecha": "%Y-%m-%d %H:%M:%S",
"incluir_metadatos": True,
"encoding": "utf-8"
}
}
Estructuras JSON Comunes
1. Lista Simple
[
{"ip": "192.168.1.1", "ruta": "/home/user/script.sh"},
{"ip": "10.0.0.1", "ruta": "/opt/scripts/test.sh"}
]
2. Objeto con Datos y Metadatos
{
"datos": [
{"ip": "192.168.1.1", "ruta": "/home/user/script.sh"},
{"ip": "10.0.0.1", "ruta": "/opt/scripts/test.sh"}
],
"metadatos": {
"total": 2,
"fecha": "2024-01-15 14:30:25"
}
}
3. Respuesta de API
{
"estado": "exito",
"mensaje": "Datos exportados correctamente",
"datos": [...],
"paginacion": {
"pagina": 1,
"total_paginas": 10,
"registros_por_pagina": 100
}
}
Manejo de Errores
Errores Comunes
import json
# Error de sintaxis JSON
try:
datos = json.loads('{"clave": "valor",}') # Coma extra
except json.JSONDecodeError as e:
print(f"Error de sintaxis JSON: {e}")
# Error de tipo de datos
try:
datos = json.dumps({"fecha": datetime.now()}) # datetime no es serializable
except TypeError as e:
print(f"Error de tipo: {e}")
# Error de archivo
try:
with open("archivo.json", "r") as f:
datos = json.load(f)
except FileNotFoundError:
print("Archivo no encontrado")
Serialización Personalizada
from datetime import datetime
class DateTimeEncoder(json.JSONEncoder):
"""Encoder personalizado para datetime."""
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
# Usar encoder personalizado
datos = {"fecha": datetime.now()}
json_string = json.dumps(datos, cls=DateTimeEncoder)
Casos de Uso Avanzados
1. Configuración de Aplicación
def cargar_configuracion_json(archivo_config):
"""Carga configuración desde archivo JSON."""
try:
with open(archivo_config, 'r', encoding='utf-8') as f:
config = json.load(f)
return config
except Exception as e:
print(f"Error al cargar configuración: {e}")
return {}
# Uso
config = cargar_configuracion_json("config.json")
2. Logging de Metadatos
def log_metadatos_exportacion(df, archivo_salida, config):
"""Registra metadatos de la exportación."""
metadatos = {
"timestamp": datetime.now().isoformat(),
"archivo_salida": archivo_salida,
"registros_exportados": len(df),
"columnas": list(df.columns),
"configuracion_usada": config
}
# Guardar metadatos en archivo separado
with open("metadatos_exportacion.json", "w") as f:
json.dump(metadatos, f, indent=2)
3. Validación de Estructura
def validar_estructura_json(datos, esquema_esperado):
"""Valida que los datos JSON tengan la estructura esperada."""
def verificar_estructura(datos, esquema):
if isinstance(esquema, dict):
if not isinstance(datos, dict):
return False
for clave in esquema:
if clave not in datos:
return False
if not verificar_estructura(datos[clave], esquema[clave]):
return False
elif isinstance(esquema, list):
if not isinstance(datos, list):
return False
if datos and not verificar_estructura(datos[0], esquema[0]):
return False
return True
return verificar_estructura(datos, esquema_esperado)
# Ejemplo de uso
esquema = {
"datos": [{"ip": "", "ruta_script": ""}],
"total_registros": 0,
"metadatos": {"fecha_exportacion": ""}
}
es_valido = validar_estructura_json(datos_json, esquema)
Ventajas y Desventajas
Ventajas
- Ligero: Menos verboso que XML
- Rápido: Parsing más eficiente
- Universal: Soporte nativo en JavaScript y muchos lenguajes
- Legible: Formato fácil de leer y escribir
- Flexible: Soporte para estructuras complejas
Desventajas
- Sin comentarios: No soporta comentarios nativos
- Sin esquemas: No tiene validación de esquemas incorporada
- Sin tipos: No distingue entre enteros y flotantes
- Sin referencias: No soporta referencias circulares
Mejores Prácticas
- Usar indentación: Para archivos legibles por humanos
- Incluir metadatos: Agregar información contextual
- Manejar errores: Siempre usar try/except
- Validar datos: Verificar estructura antes de procesar
- Usar encoding UTF-8: Para soporte de caracteres especiales
- Documentar esquemas: Especificar estructura esperada
Ejercicios Prácticos
- Crear JSON desde DataFrame: Convierte un DataFrame a JSON con metadatos
- Validar estructura: Verifica que un JSON tenga la estructura esperada
- Configuración dinámica: Carga configuración desde archivo JSON
- Logging de metadatos: Registra información sobre exportaciones
Recursos Adicionales
Documentación Oficial
- Documentación oficial de json
- JSON Schema
- JSONPath para consultas
- JSON Lines (JSONL)
- JSON Web Tokens (JWT)
Bibliografía Recomendada
- Python Tricks (Dan Bader) - Capítulo sobre serialización JSON
- Effective Python (Brett Slatkin) - Item 46: Use Built-in Algorithms and Data Structures
- Python Cookbook, 3rd Ed (Beazley & Jones) - Recetas sobre JSON
- RESTful Web APIs (Richardson & Amundsen) - Uso de JSON en APIs
Conceptos Relacionados
- YAML - Otro formato de configuración
- XML - Formato estructurado alternativo
- Pathlib - Manejo moderno de archivos
Siguiente paso
Ahora que conoces JSON, aprende sobre YAML, otro formato popular para configuración. Continúa con: YAML