JSON Formatter Python — Guía de json.dumps()
Usa el Formateador y Embellecedor JSON gratuito directamente en tu navegador — sin instalación.
Probar Formateador y Embellecedor JSON online →Cuando depuro un cliente de API en Python, lo primero que busco es formatear json en python — una sola llamada a json.dumps(data, indent=4) convierte un blob ilegible en una cadena perfectamente navegable. El módulo integrado json de Python gestiona esto completamente en la biblioteca estándar — sin instalación de terceros. Si solo necesitas un resultado rápido sin escribir código, el Formateador JSON de ToolDeck lo hace al instante. Esta guía cubre todos los métodos prácticos: json.dumps() con todos sus parámetros, pprint, orjson para formateo de alto rendimiento, la CLI json.tool, serialización de tipos personalizados como datetime y UUID, procesamiento de archivos de gigabytes con ijson, y resaltado de sintaxis en terminal con rich — todo con código compatible con Python 3.8+.
- →
json.dumps(data, indent=4)está integrado en la stdlib de Python desde la versión 2.6 — sin instalación. - →Pasa
ensure_ascii=Falsesiempre que tus datos contengan letras acentuadas, caracteres CJK o emojis. - →Para
datetime,UUIDo clases personalizadas, usa el parámetrodefault=o crea una subclase dejson.JSONEncoder. - →
separators=(',', ':')elimina todos los espacios — úsalo para transmisión por red o embedding en URLs. - →
orjsones 5–10× más rápido que la stdlib y gestionadatetimeyuuid.UUIDde forma nativa. - →
pprint.pprint()produce sintaxis Python (True/None), no JSON válido — nunca lo uses para archivos o respuestas de API. - →Para archivos JSON mayores de 50 MB, usa streaming con
ijsonen lugar dejson.load()para evitarMemoryError.
¿Qué es el formateo bonito de JSON?
El formateo bonito (pretty printing) transforma una cadena JSON densa y minificada en un formato legible con sangría consistente y saltos de línea. La transformación es puramente cosmética: los datos son idénticos, solo cambia la presentación. El módulo json de Python lo gestiona completamente en la biblioteca estándar — nada que instalar.
{"id":"usr_c7a3b1","nombre":"Carlos Mendoza","roles":["admin","editor"],"config":{"tema":"oscuro","idioma":"es"}}{
"id": "usr_c7a3b1",
"nombre": "Carlos Mendoza",
"roles": [
"admin",
"editor"
],
"config": {
"tema": "oscuro",
"idioma": "es"
}
}json.dumps() — La forma estándar de formatear JSON
json.dumps() forma parte de la biblioteca estándar de Python desde la versión 2.6 — solo necesitas import json, sin instalación adicional. Serializa cualquier objeto Python compatible con JSON en una cadena formateada. El parámetro clave es indent: ponlo en 4 (o 2) para obtener una salida legible.
import json
usuario = {
"id": "usr_c7a3b1",
"nombre": "Carlos Mendoza",
"roles": ["admin", "editor"],
"config": {"tema": "oscuro", "idioma": "es"}
}
print(json.dumps(usuario, indent=4, ensure_ascii=False))
# Salida:
# {
# "id": "usr_c7a3b1",
# "nombre": "Carlos Mendoza",
# "roles": [
# "admin",
# "editor"
# ],
# "config": {
# "tema": "oscuro",
# "idioma": "es"
# }
# }Para uso en producción suele interesar sort_keys=True (salida consistente entre ejecuciones) y ensure_ascii=False (conservar caracteres acentuados del español):
import json
respuesta_api = {
"timestamp": "2024-05-01T10:30:00Z",
"estado": "éxito",
"datos": {
"user_id": "usr_c7a3b1",
"nombre_completo": "Ana Torres",
"ciudad": "Bogotá",
"puntuacion": 4892.5,
"etiquetas": ["python", "backend", "api"]
}
}
print(json.dumps(respuesta_api, indent=4, sort_keys=True, ensure_ascii=False))
# Salida (claves ordenadas, acentos preservados):
# {
# "datos": {
# "ciudad": "Bogotá",
# "etiquetas": ["api", "backend", "python"],
# "nombre_completo": "Ana Torres",
# "puntuacion": 4892.5,
# "user_id": "usr_c7a3b1"
# },
# "estado": "éxito",
# "timestamp": "2024-05-01T10:30:00Z"
# }json.dumps() devuelve una cadena. Para escribir JSON formateado directamente en un archivo, usa json.dump(data, f, indent=4) (sin la s) — escribe en el objeto archivo y evita crear una cadena intermedia en memoria.Referencia de parámetros de json.dumps()
Todos los parámetros son opcionales excepto el objeto en sí. Los valores por defecto producen JSON compacto y seguro en ASCII — pasa los parámetros explícitamente para obtener una salida legible por humanos.
Salida JSON compacta con el parámetro separators
Por defecto json.dumps() separa los elementos con ", " y las claves de los valores con ": ". El parámetro separators permite modificar ambos. Pasar (',', ':') elimina todos los espacios y produce el JSON válido más compacto posible — útil para transmisión por red, embedding en URLs o almacenamiento en columnas de base de datos.
import json
payload = {
"endpoint": "/api/v2/eventos",
"filtros": {"estado": "activo", "limite": 100},
"orden": "desc"
}
# Por defecto — espacios tras separadores (legible)
salida_normal = json.dumps(payload, ensure_ascii=False)
# {"endpoint": "/api/v2/eventos", "filtros": {"estado": "activo", "limite": 100}, "orden": "desc"}
# len = 88
# Compacto — sin espacios
salida_compacta = json.dumps(payload, separators=(',', ':'), ensure_ascii=False)
# {"endpoint":"/api/v2/eventos","filtros":{"estado":"activo","limite":100},"orden":"desc"}
# len = 80 (9% más pequeño; el ahorro crece con payloads más grandes)
# Compacto + claves ordenadas para hashes reproducibles
canonico = json.dumps(payload, separators=(',', ':'), sort_keys=True, ensure_ascii=False)
print(canonico)
# {"endpoint":"/api/v2/eventos","filtros":{"estado":"activo","limite":100},"orden":"desc"}indent= junto con separators=, el argumento separators solo controla los separadores en línea — los saltos de línea y la sangría de indent se conservan. Si quieres una salida compacta de una sola línea, omite indent (o pasa None) y establece separators=(',', ':').Serialización de objetos Python personalizados con el parámetro default
El módulo estándar json serializa dicts, listas, cadenas, números, booleanos y None — pero lanza un TypeError para cualquier otro tipo. Los dos culpables más habituales en código de producción son los objetos datetime y los UUID.
import json
from datetime import datetime, timezone
import uuid
pedido = {
"pedido_id": uuid.uuid4(), # ❌ TypeError: UUID is not JSON serializable
"realizado_en": datetime.now(timezone.utc), # ❌ TypeError: datetime is not JSON serializable
"total_eur": 142.50,
"articulos": ["suscripcion-pro", "addon-almacenamiento"]
}
json.dumps(pedido) # lanza TypeErrorEnfoque 1 — el parámetro default=
Pasa un callable al parámetro default=. json.dumps() lo llama para cualquier objeto que no sepa manejar. Devuelve una representación serializable, o lanza TypeError para los tipos que no soportes explícitamente — nunca ignores en silencio los tipos desconocidos.
import json
from datetime import datetime, timezone, date
import uuid
from decimal import Decimal
def json_por_defecto(obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
if isinstance(obj, uuid.UUID):
return str(obj)
if isinstance(obj, Decimal):
return float(obj)
raise TypeError(f"El tipo {type(obj).__name__!r} no es serializable en JSON")
pedido = {
"pedido_id": uuid.uuid4(),
"realizado_en": datetime(2024, 5, 1, 10, 30, 0, tzinfo=timezone.utc),
"total_eur": Decimal("142.50"),
"articulos": ["suscripcion-pro", "addon-almacenamiento"]
}
print(json.dumps(pedido, indent=4, default=json_por_defecto, ensure_ascii=False))
# {
# "pedido_id": "a3f1c2d4-e5b6-7890-abcd-ef1234567890",
# "realizado_en": "2024-05-01T10:30:00+00:00",
# "total_eur": 142.5,
# "articulos": ["suscripcion-pro", "addon-almacenamiento"]
# }Enfoque 2 — subclase de json.JSONEncoder
Para lógica de codificación reutilizable compartida entre varios módulos, crear una subclase de json.JSONEncoder es más limpio que propagar una función default por todos lados. Sobreescribe el método default y llama a super().default(obj) como último recurso — esto preserva el comportamiento correcto de error para tipos no soportados.
import json
from datetime import datetime, timezone
import uuid
from decimal import Decimal
class AppEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, uuid.UUID):
return str(obj)
if isinstance(obj, Decimal):
return float(obj)
return super().default(obj) # lanza TypeError para tipos desconocidos
pedido = {
"pedido_id": uuid.uuid4(),
"realizado_en": datetime(2024, 5, 1, 10, 30, 0, tzinfo=timezone.utc),
"total_eur": Decimal("142.50"),
}
# Pasa la clase encoder con cls=
print(json.dumps(pedido, indent=4, cls=AppEncoder, ensure_ascii=False))
# Salida idéntica al enfoque con default=super().default(obj) (o lanza TypeError explícitamente) para los tipos no reconocidos. Devolver silenciosamente str(obj) para todo corrumpirá objetos que deberían haber lanzado un error — un bug difícil de rastrear en producción.Decodificando de vuelta — object_hook
Codificar es solo la mitad del proceso. Para reconstruir un objeto Python personalizado desde JSON, pasa una función object_hook a json.loads() o json.load(). El hook se invoca para cada objeto JSON decodificado (dict) y puede devolver cualquier valor Python — permitiendo un ciclo completo de codificación ↔ decodificación.
import json
from datetime import datetime
from dataclasses import dataclass
@dataclass
class Evento:
nombre: str
ocurrido_en: datetime
user_id: str
def codificar_evento(obj):
if isinstance(obj, Evento):
return {
"__tipo__": "Evento",
"nombre": obj.nombre,
"ocurrido_en": obj.ocurrido_en.isoformat(),
"user_id": obj.user_id,
}
raise TypeError(f"No se puede serializar {type(obj)}")
def decodificar_evento(d):
if d.get("__tipo__") == "Evento":
return Evento(
nombre=d["nombre"],
ocurrido_en=datetime.fromisoformat(d["ocurrido_en"]),
user_id=d["user_id"],
)
return d
# Codificar
evento = Evento("login", datetime(2024, 5, 1, 10, 30), "usr_c7a3b1")
json_str = json.dumps(evento, default=codificar_evento, indent=4)
# Decodificar de vuelta a una instancia de Evento
restaurado = json.loads(json_str, object_hook=decodificar_evento)
print(type(restaurado)) # <class 'Evento'>
print(restaurado.ocurrido_en) # 2024-05-01 10:30:00object_hook se invoca para cada dict anidado en el documento — no solo el de nivel superior. Incluye un campo discriminador (como "__tipo__") para que el hook pueda distinguir tus objetos personalizados de los dicts normales que deben quedar como están.pprint — El módulo alternativo (y cuándo no usarlo)
El módulo pprint de Python (pretty printer) formatea estructuras de datos Python para legibilidad en el terminal. Opera sobre objetos Python parseados, no sobre cadenas JSON — y su salida usa sintaxis Python, no sintaxis JSON.
import json, pprint
raw = '{"sensor_id":"s-441","lecturas":[23.1,23.4,22.9],"unidad":"celsius","activo":true}'
data = json.loads(raw)
# pprint — repr Python válido, NO JSON válido
pprint.pprint(data, sort_dicts=False)
# {'sensor_id': 's-441',
# 'lecturas': [23.1, 23.4, 22.9],
# 'unidad': 'celsius',
# 'activo': True} ← Python True, no JSON true
# json.dumps — JSON válido
print(json.dumps(data, indent=4, ensure_ascii=False))
# {
# "sensor_id": "s-441",
# "lecturas": [23.1, 23.4, 22.9],
# "unidad": "celsius",
# "activo": true ← JSON válido
# }pprint a un endpoint de API ni la escribas en un archivo .json — romperá cualquier parser JSON que espere sintaxis estándar. Usa json.dumps(indent=4) para toda salida que deba ser JSON válido.Cuándo sí tiene sentido usar pprint: inspección rápida en el REPL o en logs de depuración, especialmente cuando el objeto contiene tipos no serializables en JSON (sets, instancias de clases, dataclasses antes de la conversión).
Cómo formatear bonito una respuesta JSON de Requests
El escenario más habitual en el mundo real: tienes un archivo JSON en disco o una respuesta HTTP de una API y quieres formatearlo para depuración o logging. Ambos casos usan el mismo enfoque — parsear a un dict Python y luego formatear con json.dumps().
Leyendo desde un archivo
import json
try:
with open("config.json", "r", encoding="utf-8") as f:
data = json.load(f)
# Imprimir en consola con formato
print(json.dumps(data, indent=4, ensure_ascii=False))
# O escribir la versión formateada de vuelta al disco
with open("config.pretty.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)
except json.JSONDecodeError as e:
print(f"JSON inválido: {e}")
except FileNotFoundError:
print(f"Archivo no encontrado: config.json")Formateando una respuesta de API
import json, requests
from requests.exceptions import HTTPError, ConnectionError, Timeout
def imprimir_api(url: str) -> None:
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
print(json.dumps(resp.json(), indent=4, ensure_ascii=False))
except HTTPError as e:
print(f"HTTP {e.response.status_code}: {e}")
except (ConnectionError, Timeout) as e:
print(f"Error de red: {e}")
except json.JSONDecodeError:
print(f"El cuerpo de la respuesta no es JSON:\n{resp.text[:500]}")
imprimir_api("https://api.github.com/repos/python/cpython")response.json() ya parsea el cuerpo de la respuesta — no es necesario llamar a json.loads() por separado. Añade siempre raise_for_status() antes de acceder a .json() para capturar errores 4xx/5xx antes de que causen un confuso error de parseo.Formateo desde la línea de comandos
Python incluye json.tool, un módulo CLI para formatear JSON directamente desde el terminal — sin necesidad de escribir código Python. Está disponible en cualquier máquina con Python instalado.
# Formatear un archivo local
python -m json.tool config.json
# Canalizar respuesta de API por el formateador
curl -s https://api.github.com/users/gvanrossum | python -m json.tool
# Formatear desde stdin
echo '{"servicio":"api-gateway","version":"2.1.0","activo":true}' | python -m json.tool
# Ordenar claves alfabéticamente
python -m json.tool --sort-keys data.json
# Sangría personalizada (Python 3.9+)
python -m json.tool --indent 2 data.json--indent y --no-indent. Para filtrado JSON más potente en el terminal, considera jq — pero python -m json.tool cubre el caso de uso de formateo sin dependencias adicionales.Si no estás en un terminal — pegando una respuesta de Postman o un archivo de log — el Formateador JSON de ToolDeck te permite pegar, formatear y copiar en un solo paso, con resaltado de sintaxis y validación integrados.
Bibliotecas alternativas: orjson y rich
orjson — 5–10× más rápido con soporte nativo de tipos
El módulo estándar json es suficientemente rápido para la mayoría de casos de uso, pero si serializas miles de objetos por segundo — pipelines de logging, APIs de alto throughput, exportaciones masivas de datos — orjson es 5–10× más rápido. También gestiona de forma nativa tipos que la biblioteca estándar no puede serializar sin una función default personalizada: datetime, uuid.UUID, arrays de numpy y dataclasses.
pip install orjson
import orjson
from datetime import datetime, timezone
import uuid
evento = {
"evento_id": uuid.uuid4(), # no hace falta str() — orjson gestiona UUID
"timestamp": datetime.now(timezone.utc), # no hace falta isoformat()
"servicio": "auth-service",
"nivel": "INFO",
"payload": {
"user_id": "usr_c7a3b1",
"accion": "login",
"ip": "192.168.1.42",
"latencia_ms": 34
}
}
# orjson.dumps devuelve bytes; .decode() lo convierte a str
print(orjson.dumps(evento, option=orjson.OPT_INDENT_2).decode())
# {
# "evento_id": "a3f1c2d4-e5b6-7890-abcd-ef1234567890",
# "timestamp": "2024-05-01T10:30:00+00:00",
# "servicio": "auth-service",
# ...
# }Dos cosas a tener en cuenta: orjson.dumps() devuelve bytes, no una cadena — llama a .decode() si necesitas una cadena. Solo admite sangría de 2 espacios con OPT_INDENT_2; para salida con 4 espacios usa el estándar json.dumps(indent=4).
rich — Resaltado de sintaxis en el terminal
Si inspeccionas JSON habitualmente en el terminal o en un REPL, rich renderiza una salida con colores y resaltado de sintaxis que hace que las estructuras profundamente anidadas sean legibles de un vistazo. Las claves, cadenas, números y booleanos tienen cada uno un color diferente — mucho más fácil de escanear que una pared de texto monocromático. Es una herramienta solo para depuración, no para serialización en producción.
pip install rich
from rich import print_json
import json
# print_json() acepta una cadena JSON
raw = '{"evento":"login","user_id":"usr_c7a3b1","timestamp":"2024-05-01T10:30:00Z","exito":true,"meta":{"ip":"192.168.1.42","intentos":1}}'
print_json(raw)
# Para formatear un dict Python, conviértelo primero a cadena
datos = {
"estado": "éxito",
"total": 42,
"etiquetas": ["python", "api", "backend"]
}
print_json(json.dumps(datos, ensure_ascii=False))rich.print_json() emite códigos de escape ANSI para el color del terminal — nunca captures esta salida y la escribas en un archivo .json ni la envíes como respuesta de API. Usa json.dumps(indent=4) para cualquier salida legible por máquinas.simplejson — Reemplazo compatible con la stdlib
simplejson es la biblioteca que se convirtió en el módulo json de Python — se mantiene de forma independiente y va por delante de la stdlib en funcionalidades menores. Es un reemplazo directo: cambia el import y el resto del código no varía. Útil cuando necesitas soporte de Decimal sin un encoder personalizado, o cuando el entorno Python es antiguo.
pip install simplejson
import simplejson as json # API idéntica a la stdlib
from decimal import Decimal
pedido = {
"articulo": "Suscripción API Madrid",
"precio": Decimal("49.99"), # stdlib json lanzaría TypeError aquí
"cantidad": 3,
}
# simplejson serializa Decimal de forma nativa — sin default= necesario
print(json.dumps(pedido, indent=4, use_decimal=True, ensure_ascii=False))
# {
# "articulo": "Suscripción API Madrid",
# "precio": 49.99,
# "cantidad": 3
# }orjson es la mejor opción. Usa simplejson cuando necesites serialización nativa de Decimal sin escribir un encoder personalizado, o cuando mantengas una base de código que ya lo utiliza.Procesando archivos JSON grandes sin quedarse sin memoria
json.load() lee el archivo completo en memoria antes de que puedas acceder a un solo campo. En un archivo con millones de registros o un payload de más de un gigabyte, eso provoca un MemoryError — o en el mejor caso fuerza al proceso a usar swap y funcionar muy lentamente.
Streaming con ijson
ijson es un parser JSON de streaming que genera elementos de uno en uno desde un objeto archivo. Iteras sobre los elementos del array sin cargar nunca el dataset completo en memoria — el uso máximo de memoria es proporcional a un único objeto, no al tamaño del archivo.
pip install ijson
import ijson
from decimal import Decimal
# eventos.json tiene estructura: {"eventos": [...millones de objetos...]}
ingresos_totales = Decimal("0")
conteo_logins = 0
with open("eventos.json", "rb") as f: # ijson requiere modo binario
for evento in ijson.items(f, "eventos.item"):
if evento.get("tipo") == "compra":
ingresos_totales += Decimal(str(evento["importe_eur"]))
elif evento.get("tipo") == "login":
conteo_logins += 1
print(f"Ingresos: €{ingresos_totales:.2f} | Logins: {conteo_logins}")
# Procesa un archivo de 2 GB con ~30 MB de uso máximo de memoriajson.load() a ijson cuando tu archivo supere aproximadamente 50–100 MB. Por debajo de ese umbral, json.load() es más simple y significativamente más rápido al usar un parser en C internamente. Por encima de 100 MB, el ahorro de memoria del streaming compensa el coste adicional.NDJSON — un objeto JSON por línea
NDJSON (JSON delimitado por saltos de línea, también llamado JSON Lines o .jsonl) almacena un objeto JSON completo por línea. Los exportadores de logs, consumidores de Kafka y pipelines de datos producen frecuentemente este formato porque cada línea puede añadirse y leerse de forma independiente. La biblioteca estándar lo gestiona sin dependencias adicionales.
import json
# Escribir NDJSON — un evento por línea
eventos = [
{"ts": "2024-05-01T10:00:00Z", "usuario": "usr_c7a3b1", "accion": "login"},
{"ts": "2024-05-01T10:01:03Z", "usuario": "usr_c7a3b1", "accion": "compra", "sku": "plan-pro"},
{"ts": "2024-05-01T10:15:42Z", "usuario": "usr_4ab1d9", "accion": "login"},
]
with open("eventos.ndjson", "w", encoding="utf-8") as f:
for evento in eventos:
f.write(json.dumps(evento, ensure_ascii=False) + "\n")
# Leer NDJSON — memoria constante, sin importar el tamaño del archivo
conteo_compras = 0
with open("eventos.ndjson", "r", encoding="utf-8") as f:
for linea in f:
linea = linea.strip()
if not linea: # saltar líneas en blanco
continue
evento = json.loads(linea)
if evento.get("accion") == "compra":
conteo_compras += 1
print(f"{evento['ts']} — {evento['usuario']} compró {evento['sku']}")Errores Comunes
He visto estos cuatro errores en casi todas las revisiones de código que involucran serialización JSON — especialmente en desarrolladores que vienen de JavaScript donde JSON.stringify gestiona la codificación automáticamente.
Problema: print() sobre un dict usa repr de Python — la salida muestra True/None (sintaxis Python), no true/null (sintaxis JSON). No es JSON válido.
Solución: Usa siempre json.dumps(data, indent=4) para una salida JSON válida y legible.
data = {"activo": True, "contador": None}
print(data)
# {'activo': True, 'contador': None}print(json.dumps(data, indent=4))
# {
# "activo": true,
# "contador": null
# }Problema: Los caracteres especiales (letras acentuadas, emojis) se escapan a secuencias \\uXXXX, haciendo la salida ilegible.
Solución: Pasa ensure_ascii=False para preservar los caracteres Unicode originales.
usuario = {"nombre": "Ana Torres", "ciudad": "Bogotá"}
json.dumps(usuario, indent=2)
# {"nombre": "Ana Torres", "ciudad": "Bogot\u00e1"}json.dumps(usuario, indent=2, ensure_ascii=False)
# {"nombre": "Ana Torres", "ciudad": "Bogotá"}Problema: json.dumps() devuelve una cadena; luego necesitas una llamada f.write() separada, creando una cadena intermedia innecesaria.
Solución: Usa json.dump(data, f, indent=4) — escribe directamente en el objeto archivo.
with open("salida.json", "w") as f:
f.write(json.dumps(data, indent=4))with open("salida.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=4, ensure_ascii=False)Problema: pprint.pprint() usa sintaxis Python (True, None, comillas simples) que los parsers JSON rechazan.
Solución: Usa json.dumps(indent=4) para cualquier salida que deba parsearse como JSON.
import pprint
pprint.pprint({"ejecutando": True, "ultimo_error": None})
# {'ejecutando': True, 'ultimo_error': None}import json
print(json.dumps({"ejecutando": True, "ultimo_error": None}, indent=4))
# {"ejecutando": true, "ultimo_error": null}Comparación de métodos — json.dumps, orjson, simplejson, rich
Usa json.dumps() para formateo cotidiano y escritura de archivos — cubre el 95% de casos sin dependencias. Recurre a orjson cuando serialices en un hot path o cuando tus objetos incluyan datetime y UUID. Usa simplejson cuando necesites compatibilidad directa con la stdlib y soporte de Decimal listo para usar. Reserva rich.print_json() y pprint estrictamente para inspección local en el terminal — ninguno produce salida legible por máquinas.
Preguntas Frecuentes
¿Cómo formateo JSON en Python de forma legible?
Llama a json.dumps(data, indent=4). El parámetro indent define los espacios por nivel de anidación. Importa el módulo json primero — viene incluido en la biblioteca estándar de Python, no necesitas pip install. Pasa ensure_ascii=False si tus datos contienen caracteres acentuados, CJK u otros no-ASCII.
import json
usuario = {"username": "cmendoza", "plan": "enterprise", "permisos": ["lectura", "escritura", "despliegue"]}
print(json.dumps(usuario, indent=4, ensure_ascii=False))¿Cuál es la diferencia entre json.dumps() y json.dump()?
json.dumps() (con "s") devuelve una cadena formateada en memoria. json.dump() (sin "s") escribe directamente en un objeto archivo — pasa el manejador del archivo abierto como segundo argumento. Para escribir JSON formateado en disco, json.dump(data, f, indent=4) es el patrón idiomático y evita crear una cadena intermedia.
# dumps → cadena en memoria
formateado = json.dumps(data, indent=4)
# dump → escribe directamente al archivo
with open('salida.json', 'w', encoding='utf-8') as f:
json.dump(data, f, indent=4)¿Por qué json.dumps() muestra \u00e1\u00f3\u00fa en lugar del carácter real?
Por defecto ensure_ascii=True escapa todos los caracteres no ASCII a secuencias \uXXXX. Pasa ensure_ascii=False para preservar los caracteres Unicode originales. Esto es especialmente importante para nombres, direcciones y cualquier contenido generado por usuarios en español u otros idiomas no latinos.
data = {"ciudad": "Bogotá", "pais": "México", "saludo": "¡Hola!"}
# Por defecto — escapado
json.dumps(data, indent=4)
# {"ciudad": "Bogot\u00e1", "pais": "M\u00e9xico", ...}
# Legible
json.dumps(data, indent=4, ensure_ascii=False)
# {"ciudad": "Bogotá", "pais": "México", "saludo": "¡Hola!"}¿Cómo formateo una cadena JSON (no un dict)?
Primero parsea la cadena con json.loads(), luego formatea con json.dumps(). Las dos llamadas se pueden encadenar en una línea para inspección rápida en el terminal.
import json
raw = '{"endpoint":"/api/v2/pedidos","timeout":30,"reintentar":true}'
print(json.dumps(json.loads(raw), indent=4))¿Puedo usar pprint para formatear JSON en Python?
pprint.pprint() produce representación de objetos Python, no JSON válido. Usa True/False/None (sintaxis Python) en lugar de true/false/null (sintaxis JSON). Nunca pases la salida de pprint a una API o parser JSON — usa json.dumps(indent=4) para cualquier salida que deba ser JSON válido.
import pprint, json
data = {"activo": True, "puntuacion": None}
pprint.pprint(data) # {'activo': True, 'puntuacion': None} ← no es JSON
json.dumps(data, indent=4) # {"activo": true, "puntuacion": null} ← JSON válido¿Cómo ordeno las claves JSON alfabéticamente en Python?
Añade sort_keys=True a json.dumps(). Para la línea de comandos, usa python -m json.tool --sort-keys data.json. Las claves ordenadas hacen los diffs de JSON legibles y facilitan detectar qué cambió entre versiones.
import json
servidor = {"workers": 4, "host": "0.0.0.0", "puerto": 8080, "debug": False}
print(json.dumps(servidor, indent=4, sort_keys=True, ensure_ascii=False))
# {
# "debug": false,
# "host": "0.0.0.0",
# "puerto": 8080,
# "workers": 4
# }Python te da control total — serializadores personalizados, streaming, integración en pipelines. Cuando solo necesitas inspeccionar o compartir un fragmento formateado, el Formateador JSON de ToolDeck es el camino más rápido: pega tu JSON y obtén un resultado indentado y resaltado sin configurar ningún entorno.
Herramientas Relacionadas
Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.
Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.