json y Serialización
El módulo json codifica objetos de Python en texto JSON y analiza JSON en escalares de tipo dict/list. Personaliza con codificadores default y object_hook para viajes de ida y vuelta; para APIs, prefiere Pydantic 2 en los límites.
Receta
import json
from datetime import datetime, timezone
def default(obj):
if isinstance(obj, datetime):
return obj.astimezone(timezone.utc).isoformat()
raise TypeError(type(obj))
payload = json.dumps({"at": datetime.now(timezone.utc)}, default=default)Cuándo usar esto:
- Archivos de configuración y salida JSON de CLI
- REST simple sin la sobrecarga de Pydantic
json.loadsen cargas pequeñas de confianza- Líneas de registro NDJSON
- Interoperabilidad con clientes JavaScript
Ejemplo de Trabajo
import json
from dataclasses import asdict, dataclass
from datetime import datetime, timezone
from pathlib import Path
@dataclass
class Event:
name: str
at: datetime
def encode_event(obj):
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError(type(obj))
def save_events(path: Path, events: list[Event]) -> None:
raw = [asdict(e) for e in events]
path.write_text(json.dumps(raw, default=encode_event, indent=2), encoding="utf-8")
def load_events(path: Path) -> list[dict]:
return json.loads(path.read_text(encoding="utf-8"))
events = [Event("login", datetime.now(timezone.utc))]
out = Path("events.json")
save_events(out, events)
print(load_events(out))
out.unlink(missing_ok=True)Lo que esto demuestra:
- Pipeline de Dataclass -> dict -> JSON
defaultpersonalizado para tipos no nativos de JSON- Codificación explícita de archivos utf-8
- El análisis devuelve diccionarios; valida antes de usar
Inmersión Profunda
Tipos JSON
| Python | JSON |
|---|---|
| dict | object |
| list | array |
| str, int, float, bool, None | scalar |
Rendimiento
orjsonPyPI más rápido para rutas críticasjson.dumpses adecuado para CLI y configuración
Trampas Comunes
- Precisión de
float- Los números JSON son dobles IEEE. Solución:Decimalcomostrsi es dinero. - Solo
strcomo tipo de clave - Las claves de diccionario se convierten astren JSON. Solución: normalizar al cargar. - Cargas de archivos enormes - Pico de memoria. Solución: streaming con
ijsono delimitado por líneas. - Profundidad de entrada no confiable - DoS mediante objetos anidados. Solución: limitar tamaño, usar analizador con límites.
datetimesin zona horaria en JSON - ambiguo. Solución: siempre UTC ISO con desplazamiento.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Pydantic | Validación de modelos de API | Volcado interno pequeño |
| msgpack | IPC binario compacto | Necesidad de legibilidad humana |
| yaml | Configuración humana | API JSON estricta |
Preguntas Frecuentes
¿loads vs load?
loads para cadenas; load para objetos de archivo; prefiere Path.read_text + loads para mayor claridad.
¿sort_keys=True?
Diferencias estables para la configuración; tiene un costo de sobrecarga de ordenación.
¿ensure_ascii=False?
Emite caracteres UTF-8 en cadenas JSON; generalmente deseado en 3.x.
¿Serialización de Decimal?
Convertir a str en default; nunca float para dinero.
¿UUID?
str(uuid) en el codificador default.
¿Respuesta de FastAPI?
jsonable_encoder del framework; se prefieren los modelos Pydantic.
¿NDJSON?
Un json.dumps por línea escrito en archivo o stdout.
¿Validación de esquema?
jsonschema PyPI o Pydantic después de loads.
¿pickle vs json?
Nunca uses pickle con datos de red no confiables; json para intercambio.
¿indent en producción?
No; impresión bonita solo para humanos; compacto en APIs.
Relacionado
- datetime, zoneinfo y time - codificar
datetimes - Conceptos básicos de Pydantic - modelos validados
- pathlib y os - rutas de configuración
- Mejores prácticas de la biblioteca estándar - reglas de serialización
Versiones de la pila: Esta página se escribió para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+ y uv 0.6+.