JSON (JavaScript Object Notation) es el formato de intercambio de datos más utilizado en la web moderna. Casi todas las API REST devuelven JSON, los archivos de configuración usan JSON y las bases de datos NoSQL como MongoDB almacenan datos en este formato. Python ofrece soporte nativo y extremadamente potente para JSON a través del módulo json, parte de la biblioteca estándar.
En esta guía completa, aprenderás todo sobre la manipulación de datos JSON con Python: desde la serialización básica con json.dumps() y json.loads() hasta técnicas avanzadas como codificadores personalizados, validación con JSON Schema e integración con APIs REST. ¡Comencemos!
¿Qué es JSON?
JSON es un formato de texto ligero para almacenar y transportar datos estructurados. Fue especificado originalmente por Douglas Crockford en JSON.org y posteriormente estandarizado por la RFC 7159. Su sintaxis es simple y legible tanto para humanos como para máquinas.
JSON soporta solo seis tipos de datos:
- String — secuencia de caracteres entre comillas dobles
- Number — enteros o decimales (punto flotante)
- Boolean —
trueofalse - Null — valor nulo (
null) - Array — lista ordenada de valores entre corchetes
[] - Object — colección de pares clave/valor entre llaves
{}
{
"nombre": "Python",
"version": 3.12,
"paradigmas": ["Orientado a Objetos", "Funcional", "Imperativo"],
"tipado": "dinámico",
"open_source": true,
"creador": null
}
Según MDN Web Docs, JSON se destaca por su simplicidad y ubicuidad en la web, siendo soportado de forma nativa por todos los lenguajes de programación modernos.
El Módulo json de Python
Python incluye el módulo json como parte de su biblioteca estándar. Esto significa que no necesitas instalar nada — solo impórtalo y comienza a usarlo. La documentación oficial del módulo json es el punto de partida ideal para consultas en profundidad.
import json
El módulo ofrece cuatro funciones principales que forman la base de toda manipulación de JSON en Python:
json.dumps()— convierte un objeto Python en una cadena JSONjson.loads()— convierte una cadena JSON en un objeto Pythonjson.dump()— escribe un objeto Python directamente en un archivo JSONjson.load()— lee un archivo JSON y lo convierte en un objeto Python
json.dumps() — Serializando Python a JSON
La función json.dumps() serializa un objeto Python en una cadena JSON. La tabla de conversión sigue el mapeo estándar:
dict→Object{}list,tuple→Array[]str→Stringint,float→NumberTrue→trueFalse→falseNone→null
import json
datos = {
"lenguaje": "Python",
"creado_en": 1991,
"tipos": ["int", "float", "str", "list", "dict"],
"interpretado": True,
"autor": None
}
json_string = json.dumps(datos)
print(json_string)
Salida: {"lenguaje": "Python", "creado_en": 1991, "tipos": ["int", "float", "str", "list", "dict"], "interpretado": true, "autor": null}
Observa cómo True se convierte en true y None se convierte en null automáticamente, siguiendo el estándar JSON.
Parámetros Avanzados de dumps()
json.dumps() ofrece varios parámetros para controlar la salida:
import json
datos = {"nombre": "Python", "version": 3.12, "caracteristicas": ["simple", "potente"]}
indent — formatea con indentación para legibilidad
print(json.dumps(datos, indent=2))
sort_keys — ordena las claves alfabéticamente
print(json.dumps(datos, indent=2, sort_keys=True))
ensure_ascii — si es False, permite caracteres Unicode
print(json.dumps({"nombre": "João"}, ensure_ascii=False))
Salida: {"nombre": "João"}
separators — personaliza separadores (elimina espacios)
print(json.dumps(datos, separators=(",", ":")))
Salida: {"nombre":"Python","version":3.12,"caracteristicas":["simple","potente"]}
El parámetro ensure_ascii=False es esencial cuando trabajas con textos en español u otros idiomas con acentos y caracteres especiales, como explica el tutorial de Real Python sobre JSON.
json.loads() — Convirtiendo JSON a Python
La función json.loads() hace el camino inverso: recibe una cadena JSON y la convierte en un objeto Python.
import json
json_string = '{"nombre": "Python", "version": 3.12, "interpretado": true, "autor": null}'
datos = json.loads(json_string)
print(datos)
Salida: {'nombre': 'Python', 'version': 3.12, 'interpretado': True, 'autor': None}
print(type(datos))
Salida: <class 'dict'>
La conversión respeta el mapeo inverso: true se convierte en True, null en None, y las cadenas JSON en cadenas Python. El resultado es siempre un diccionario Python cuando el JSON de origen es un objeto.
json.dump() y json.load() — Trabajando con Archivos
Las funciones json.dump() y json.load() trabajan directamente con archivos, evitando la necesidad de cadenas intermedias. Este tema se complementa con la guía de Manipulación de Archivos TXT, CSV y JSON.
import json
Escribiendo JSON en un archivo
datos = {
"nombre": "Python",
"año": 1991,
"frameworks": ["Django", "Flask", "FastAPI"]
}
with open("datos.json", "w", encoding="utf-8") as archivo:
json.dump(datos, archivo, indent=2, ensure_ascii=False)
Leyendo JSON de un archivo
with open("datos.json", "r", encoding="utf-8") as archivo:
datos_cargados = json.load(archivo)
print(datos_cargados["nombre"]) # Salida: Python
Usa siempre la codificación utf-8 al trabajar con archivos JSON para garantizar la compatibilidad con caracteres acentuados y especiales.
Serializando Objetos Python Personalizados
Por defecto, el módulo json no sabe cómo serializar objetos de clases personalizadas. Intenta ejecutar el código siguiente y obtendrás un TypeError:
import json
class Persona:
def init(self, nombre, edad):
self.nombre = nombre
self.edad = edad
p = Persona("Ana", 30)
print(json.dumps(p)) # TypeError: Object of type Persona is not JSON serializable
Existen dos enfoques principales para resolver esto.
Método 1: Usando un JSONEncoder Personalizado
Crea una subclase de json.JSONEncoder y sobrescribe el método default(). La documentación oficial de JSONEncoder proporciona todos los detalles de este enfoque.
import json
from datetime import datetime
class Persona:
def init(self, nombre, edad):
self.nombre = nombre
self.edad = edad
class CodificadorPersonalizado(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Persona):
return {"nombre": obj.nombre, "edad": obj.edad, "_tipo": "Persona"}
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
p = Persona("Ana", 30)
ahora = datetime.now()
print(json.dumps({"persona": p, "ahora": ahora}, cls=CodificadorPersonalizado, indent=2))
Método 2: Usando Pydantic
Pydantic es una biblioteca que ofrece validación de datos y serialización nativa a JSON. Es ampliamente utilizada con FastAPI y es el enfoque recomendado para proyectos profesionales.
from pydantic import BaseModel
class Persona(BaseModel):
nombre: str
edad: int
p = Persona(nombre="Ana", edad=30)
print(p.model_dump_json(indent=2))
Salida: {"nombre": "Ana", "edad": 30}
El método model_dump_json() de Pydantic ya devuelve una cadena JSON válida, resolviendo la serialización de forma elegante y segura.
Manejo de Errores y Excepciones
Al trabajar con datos JSON, especialmente aquellos provenientes de fuentes externas, es fundamental manejar los errores de análisis. El módulo json lanza excepciones específicas que deben ser capturadas:
import json
def procesar_json(texto):
try:
datos = json.loads(texto)
return datos
except json.JSONDecodeError as e:
print(f"Error al decodificar JSON: {e}")
print(f"Posición del error: línea {e.lineno}, columna {e.colno}")
return None
JSON inválido
json_invalido = '{"nombre": "Python", version: 3.12}'
resultado = procesar_json(json_invalido)
Salida: Error al decodificar JSON: ...
JSON válido
json_valido = '{"nombre": "Python"}'
resultado = procesar_json(json_valido)
print(resultado)
Salida: {'nombre': 'Python'}
Valida siempre el JSON recibido antes de procesarlo. La combinación de try/except con json.JSONDecodeError garantiza que tu aplicación no se rompa al recibir datos malformados.
Validación con JSON Schema
Para garantizar que un JSON siga una estructura específica, puedes usar JSON Schema. Esta especificación define contratos formales para la estructura de documentos JSON, algo esencial en APIs e integraciones entre sistemas.
from jsonschema import validate, ValidationError
schema = {
"type": "object",
"properties": {
"nombre": {"type": "string", "minLength": 1},
"edad": {"type": "integer", "minimum": 0},
"email": {"type": "string", "format": "email"}
},
"required": ["nombre", "email"]
}
datos_validos = {"nombre": "Ana", "edad": 30, "email": "[email protected]"}
validate(instance=datos_validos, schema=schema) # OK, no lanza excepción
datos_invalidos = {"nombre": "", "email": "invalido"}
try:
validate(instance=datos_invalidos, schema=schema)
except ValidationError as e:
print(f"Error de validación: {e.message}")
Aunque la biblioteca jsonschema debe instalarse por separado (pip install jsonschema), es extremadamente útil para validar payloads de API, archivos de configuración y cualquier dato JSON que requiera una estructura predecible.
JSON y APIs REST
JSON es el formato estándar de entrada y salida de la mayoría de las APIs REST modernas. Cuando haces una solicitud HTTP a una API, la respuesta casi siempre llega en JSON. Así es como se consume y envía JSON usando la biblioteca requests:
import json
import requests
Consumiendo una API que devuelve JSON
response = requests.get("https://api.github.com/users/python")
datos = response.json() # Equivalente a json.loads(response.text)
print(f"Repositorios públicos: {datos['public_repos']}")
Enviando JSON a una API
payload = {"titulo": "Nuevo Post", "contenido": "JSON con Python"}
headers = {"Content-Type": "application/json"}
response = requests.post(
"https://jsonplaceholder.typicode.com/posts",
data=json.dumps(payload),
headers=headers
)
print(response.json())
Para dominar completamente el consumo de APIs, consulta la guía completa sobre Python Requests para solicitudes HTTP.
FastAPI, uno de los frameworks web más populares de Python, usa Pydantic para validar y serializar JSON automáticamente. La documentación de FastAPI sobre JSON Encoder muestra cómo el framework maneja tipos personalizados de forma transparente.
Buenas Prácticas y Rendimiento
Al trabajar con JSON en Python, sigue estas recomendaciones:
- Usa siempre
ensure_ascii=Falsecuando haya caracteres acentuados o Unicode en el contenido - Define
indentsolo en archivos de configuración — en producción, omítelo para reducir el tamaño de los datos transferidos - Usa
separators=(",", ":")en APIs para minimizar el payload JSON - Prefiere Pydantic o dataclasses para serialización de objetos en proyectos grandes
- Valida datos con JSON Schema cuando la estructura deba garantizarse
- Maneja excepciones con
json.JSONDecodeErroral procesar datos de fuentes externas
Para proyectos que exigen rendimiento máximo, considera alternativas como orjson (escrito en Rust) o ujson. El módulo nativo json de Python, sin embargo, es suficiente para la gran mayoría de los casos de uso y tiene la ventaja de no requerir dependencias externas.
El tutorial de W3Schools sobre Python JSON ofrece ejemplos adicionales y ejercicios prácticos para reforzar el aprendizaje.
Conclusión
JSON y Python forman una combinación poderosa y omnipresente en el desarrollo de software moderno. El módulo json de la biblioteca estándar ofrece todas las herramientas necesarias para serializar, deserializar, leer y escribir datos JSON con solo unas pocas líneas de código.
En esta guía, aprendiste:
- Los fundamentos del formato JSON y sus tipos de datos
- Las cuatro funciones principales:
dumps(),loads(),dump(),load() - Cómo serializar objetos Python personalizados con JSONEncoder y Pydantic
- Cómo manejar errores de análisis y validar estructuras con JSON Schema
- Cómo integrar JSON con APIs REST usando la biblioteca requests
- Buenas prácticas para rendimiento y seguridad
Con este conocimiento, estás preparado para manipular datos JSON en cualquier proyecto Python — ya sea una aplicación web, un script de automatización, una API REST o un pipeline de datos. Para profundizar aún más, explora la documentación oficial y practica con datos reales de APIs públicas.