JSON en 10 Minutos

JSON es un formato de datos diminuto con puntuación estricta y casos extremos sorprendentemente costosos. Aprende los seis valores que puede representar, valida en las fronteras del sistema, y deja de fingir que las fechas o los enteros gigantes son tipos nativos de JSON.

🎙️ Publicado y grabado: ·

01JSON tiene seis tipos de valor

JSON puede contener un objeto, array, string, número, booleano o null. Ese es todo el sistema de tipos. No hay fecha, byte array, set, comentario, undefined ni tipo entero especial. Los objetos mapean claves string a valores. Los arrays preservan el orden. El orden de las claves de un objeto nunca debería tener significado, aunque muchos parsers lo conserven por casualidad.

{
  "name": "Mina",
  "score": 9.5,
  "active": true,
  "middle_name": null,
  "skills": ["SQL", "Python"],
  "address": { "city": "Leeds" }
}
Null no es ausencia

{"middle_name": null} proporciona explícitamente una clave sin valor. {} omite la clave. Las APIs a menudo usan esa distinción para "borrar este campo" frente a "dejarlo como está". Decide el significado en tu contrato; no dejes que cada cliente adivine.

02La sintaxis es estricta a propósito

Las claves y strings requieren comillas dobles. Las comas separan miembros, pero una coma final es ilegal. Los comentarios son ilegales. Los números no pueden ser NaN ni Infinity. JSON es un formato de transmisión, no un lenguaje de configuración amigable para edición manual. Si la gente edita el archivo a diario, usa TOML o YAML y valídalo; no inventes JSON con comentarios.

// Invalid JSON
{ 'port': 3000, "debug": true, }

// Valid JSON
{ "port": 3000, "debug": true }
Unexpected token, solucionado

Chrome puede reportar SyntaxError: Unexpected token ' in JSON at position 2. Uno: inspecciona el carácter en la posición indicada. Dos: reemplaza claves y strings con comillas simples por comillas dobles. Tres: elimina comas finales y comentarios. Cuatro: ejecuta python -m json.tool settings.json. Imprime JSON formateado si todo va bien y una línea y columna precisas si falla.

03Anida por propiedad, no por ingenio

Anidar es útil cuando el hijo pertenece al padre. Una dirección de envío pertenece dentro de un snapshot de pedido. Cincuenta niveles de data, attributes e items genéricos no hacen una API flexible; hacen que cada consumidor programe a la defensiva. Mantén los identificadores estables cerca de la raíz y usa arrays solo cuando los valores repetidos realmente tengan un orden.

{
  "order_id": "ord_204",
  "customer_id": "cus_18",
  "shipping_address": {
    "line1": "14 King Street", "city": "Leeds"
  },
  "items": [
    { "sku": "BK-7", "quantity": 2 }
  ]
}

Accede a datos anidados de forma defensiva cuando el contrato permite ausencia. En JavaScript, order.shipping_address?.city ?? "unknown" maneja un objeto faltante. Eso no excusa una forma no documentada. Publica ejemplos y un schema para que los clientes sepan qué claves son obligatorias.

04Parsea la entrada; serializa la salida

Parsear convierte texto JSON en valores del lenguaje. Serializar hace lo inverso. Mantén esa frontera visible. En JavaScript usa JSON.parse y JSON.stringify. En Python usa json.loads para un string, json.load para un archivo, y las funciones dump correspondientes para la salida. Nunca construyas JSON concatenando strings; el escapado te traicionará.

// JavaScript
const user = JSON.parse('{"name":"Mina","active":true}');
const text = JSON.stringify(user, null, 2);

# Python
import json
user = json.loads('{"name":"Mina","active":true}')
text = json.dumps(user, indent=2, ensure_ascii=False)
Dos fallos reales de serialización

Python reporta json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) para {'name':'Mina'}. Uno: confirma que la entrada es JSON, no un diccionario de Python impreso. Dos: cambia las comillas en el productor, no con un reemplazo ciego de strings. Tres: valida con python -m json.tool. JavaScript reporta TypeError: Converting circular structure to JSON cuando un objeto se referencia a sí mismo. Uno: inspecciona el ciclo nombrado en el error. Dos: serializa un objeto plano deliberado o reemplaza la referencia circular con un ID. Tres: no ocultes el ciclo con un replacer a menos que perder ese campo sea parte del contrato.

05Valida la forma con JSON Schema

JSON válido puede ser datos inútiles igualmente. Un parser acepta una cantidad negativa y una clave email mal escrita porque ambas son sintaxis legal. JSON Schema describe campos obligatorios, tipos, formatos, rangos y si se permiten propiedades desconocidas. Valida en cada frontera de confianza. Dentro de tu propio proceso, usa modelos tipados normales en vez de revalidar el mismo objeto eternamente.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["sku", "quantity"],
  "properties": {
    "sku": { "type": "string", "minLength": 1 },
    "quantity": { "type": "integer", "minimum": 1 }
  },
  "additionalProperties": false
}

Mi opción por defecto para objetos de petición pequeños es additionalProperties: false. Las erratas silenciosas son peores que un cuatrocientos claro. Para APIs públicas versionadas, añade campos de forma compatible y haz que los clientes toleren campos que no conocen. Esos objetivos chocan, así que elige la regla deliberadamente en vez de copiar un switch de schema.

06Usa JSONL para streams y logs

Un documento JSON normal es un valor completo. Un array de un millón de registros normalmente debe parsearse entero y se vuelve incómodo de ampliar. JSON Lines, también llamado JSONL o NDJSON, almacena un valor JSON completo por línea. Es mejor para logs, exportaciones, pipelines y procesamiento incremental. No es válido como un solo documento JSON ordinario, así que etiqueta el formato honestamente.

# users.jsonl
{"id":1,"name":"Mina"}
{"id":2,"name":"Luis"}
{"id":3,"name":"Asha"}

# Process one record at a time
with open("users.jsonl", encoding="utf-8") as rows:
    for line in rows:
        user = json.loads(line)
Elige según el patrón de acceso

Usa .json para un objeto de configuración o respuesta de API que debe estar completo. Usa .jsonl cuando los registros son independientes y quieres añadir, transmitir, dividir o recuperarte tras una línea defectuosa. No envuelvas líneas JSONL en comas o corchetes. Eso lo convierte de nuevo en un array y elimina la ventaja del streaming.

07JSON sobre HTTP necesita un contrato explícito

Envía JSON con Content-Type: application/json. Usa Accept: application/json cuando el servidor puede devolver varios formatos. Comprueba el status HTTP antes de parsear el body, porque un proxy puede devolver una página de error en HTML. Un parse exitoso no dice nada sobre el éxito del negocio; los códigos de estado y los campos de respuesta siguen importando.

curl --fail-with-body https://api.example.com/orders \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  --data '{"sku":"BK-7","quantity":2}'

HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8

{"order_id":"ord_204","status":"accepted"}
HTML disfrazado de JSON

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON normalmente significa que la respuesta es HTML. Uno: registra el status y el Content-Type recibido. Dos: inspecciona el body crudo. Tres: corrige la URL, la autenticación o el error de proxy que produjo la página HTML. Cuatro: parsea JSON solo cuando el contrato de respuesta dice JSON. No recortes el ángulo y reintentes el parser.

08Enteros grandes y fechas necesitan convenciones

Los números JSON no prometen un ancho de bits, pero los números de JavaScript pierden precisión entera por encima de nueve mil billones. Un identificador no es aritmética, así que envía IDs grandes como strings. JSON tampoco tiene tipo fecha. Envía un timestamp ISO 8601 claro con zona horaria, preferiblemente UTC terminado en Z. Un valor solo-fecha como un cumpleaños debería quedarse como string solo-fecha.

{
  "order_id": "9223372036854775807",
  "created_at": "2026-07-25T14:05:00Z",
  "birthday": "1994-03-12",
  "amount_minor": 1299,
  "currency": "GBP"
}

Para dinero, envía unidades menores enteras más una moneda cuando la moneda tiene una unidad menor convencional. Para precisión decimal arbitraria, envía un string decimal y documenta su escala. Nunca envíes un timestamp local como 2026-07-25 09:00 sin offset. Dos clientes pueden parsearlo como dos instantes diferentes y ambos creer que tienen razón.

09Trata JSON como entrada no confiable

Parsear JSON es más seguro que evaluar código, pero los datos resultantes siguen controlados por el remitente. Limita el tamaño del body y la profundidad de anidamiento, valida el schema, autoriza la acción solicitada y escapa los valores para el destino donde los uses. JSON no previene SQL injection, path traversal, cross-site scripting ni prototype pollution.

// Good boundary order
limitBody("256kb");
const value = JSON.parse(rawText);
validateSchema(value);
authorize(request.user, value.order_id);
await db.query("SELECT * FROM orders WHERE id = $1", [value.order_id]);

// Never do this
eval("(" + rawText + ")");
Object.assign(globalDefaults, value);
El parser no es un firewall

Rechaza bodies sobredimensionados antes de almacenarlos en buffer. Rechaza claves inesperadas antes de fusionar objetos, especialmente claves como __proto__, constructor y prototype en sistemas JavaScript. Usa queries parametrizadas. Escapa la salida para HTML al renderizarla. Redacta contraseñas, tokens y datos personales antes de hacer logging. La pregunta correcta no es "¿JSON.parse lo aceptó?" sino "¿Este valor está permitido aquí?"

10JSON cheat sheet

Ten esta referencia compacta junto al código que gestiona tu frontera.

# six values
object  {}    array  []    string  "text"
number  12.5  boolean true false  null null

# strict rules
double quotes · no trailing comma · no comments · UTF-8
no undefined · no NaN or Infinity · keys are strings

# JavaScript
JSON.parse(text)            JSON.stringify(value, null, 2)

# Python
json.loads(text)            json.dumps(value, indent=2)
python -m json.tool file.json

# HTTP and modeling
Content-Type: application/json
large ID → string           timestamp → ISO 8601 with zone
stream of records → JSONL   untrusted input → limit + validate

Mi regla con opinión es simple: JSON debe ser aburrido en la frontera. Usa strings para IDs, timestamps explícitos, schemas para datos entrantes y JSONL para streams de registros. Si tu formato necesita comentarios, referencias, literales de fecha personalizados y cinco convenciones de decodificación, ya no es un contrato JSON simple. Elige un formato que admita lo que los datos realmente son.

Tell me what missed

A correction is more useful than a compliment. This goes straight to the person who writes SwiftGrasp.

Was this page useful?
0/1000

Please do not include passwords, private keys, or personal information.