Principiante7 min de lectura

JSON es la caverna de Platón

Un validador de JSON responde a una sola pregunta: ¿está bien formado? Durante veinte años tratamos esa respuesta como si significara 'el dato es correcto'. Con un humano en cada extremo colaba. Sin él, no.

  • #json
  • #epistemologia
  • #agentes
  • #datos

La provocación

Un validador de JSON es, probablemente, el test más inútil de tu pipeline: pasa en verde con especial entusiasmo justo cuando el dato está mal.

Comprueba una cosa —que las llaves cierren, que las comas estén donde toca, que las comillas emparejen— y sin embargo, en casi todas las arquitecturas que he visto, ese 200 OK con JSON válido se trata como si significara el dato es correcto. No lo significa. Nunca lo significó.

Las sombras en la pared

En el mito de la caverna, unos prisioneros ven sombras proyectadas en un muro y toman las sombras por las cosas. No están equivocados sobre lo que ven: la sombra existe, es nítida, es reproducible, dos prisioneros distintos ven la misma. Están equivocados sobre lo que es.

Explícamelo como…

Mira esta sombra:

{ "valor": 100 }

Es JSON válido. ¿100 qué? ¿Euros, pesos, miligramos, un porcentaje, grados, unidades en stock, milisegundos? El formato no lo sabe. El validador dice que sí. El esquema, si lo tienes, dice number, que es exactamente la misma sombra con el contorno más marcado.

Añade contexto y el problema no desaparece: se disfraza.

{ "fecha": "01/02/2026", "importe": 1500, "cliente": "Acme" }

¿1 de febrero o 2 de enero? ¿1500 con IVA o sin? ¿"Acme" es un nombre para mostrar o una clave foránea? Todo esto es válido. Todo esto es ambiguo. Y la ambigüedad no está en tu código: está en el formato.

Por qué esto no dolía antes

Porque siempre hubo alguien fuera de la caverna.

El significado nunca viajó dentro del JSON. Viajaba en la documentación, en el Confluence, en el hilo de Slack donde preguntaste "oye, ¿la fecha es ISO o europea?" y alguien te contestó. JSON transportaba una forma lo bastante regular como para que una persona en cada extremo inyectara el significado a mano.

Funcionaba, y era barato. Pero conviene ver lo que era en realidad: un sistema de dos piezas, el formato y el humano. Y hemos quitado el humano.

Qué pasa cuando el que lee es un modelo

Un LLM que recibe {"fecha": "01/02/2026"} no tiene Confluence, ni Slack, ni al compañero que lleva seis años en el proyecto. Tiene el nombre de la clave y una distribución de probabilidad. Así que infiere. Y esa inferencia sale por el otro lado con el mismo aplomo con el que saldría un dato medido, porque a la salida hay otro JSON, y otro JSON tampoco tiene sitio donde decir "esto me lo estoy inventando a medias".

Ese es el mecanismo de buena parte de lo que llamamos alucinación. No es que el modelo mienta: es que el formato le exige comprometerse. Si un agente está un 20 % seguro de la dirección de un cliente, JSON le obliga a serializarla como una cadena absoluta. La duda existía antes de JSON.stringify y no existe después. La certeza no se pierde por el camino: se destruye en la serialización.

"Para eso están los esquemas"

Es la objeción obvia, y es la que menos aguanta.

JSON Schema, OpenAPI, Zod, Pydantic: todos hacen la misma cosa, que es restringir la sintaxis con más precisión. z.number() no dice euros. z.number().describe("euros") es un comentario para humanos: no se propaga cuando sumas, no se comprueba en la frontera siguiente, no impide sumar euros con pesos y obtener un número perfectamente válido.

Lo que el esquema garantizaLo que necesitabas saber
Es un númeroEs dinero, en una moneda concreta, y no se puede sumar con otra
Es una cadena con formato fechaEs un instante, en una zona horaria, y hasta cuándo sigue siendo cierto
El campo está presenteEl dato se midió, se dedujo, o se lo inventó alguien
Cumple el contratoEl emisor estaba seguro de él

Un esquema es la sombra dibujada con más detalle. Sigue siendo una sombra.

Las tres cosas que JSON estructuralmente no puede llevar

  1. Ontología. Qué clase de cosa es esto. string e int32 son categorías de memoria, no del mundo. Moneda, Medida, Instante sí lo son, y ninguna cabe en JSON salvo como acuerdo verbal entre dos equipos.
  2. Certeza y procedencia. Cuánto se lo cree quien lo envía, y de dónde salió. Un valor medido por un sensor y otro completado por un modelo son indistinguibles una vez serializados.
  3. Validez temporal. Hasta cuándo sigue siendo verdad. "El CEO de Acme es Ana" fue cierto. La verdad caduca; el campo, no.

Puedes añadir claves: "confidence": 0.2, "source": "sensor_3", "validUntil": "…". Hazlo, es mejor que nada. Pero mira bien lo que has construido: una convención que nada obliga a leer. El siguiente servicio de la cadena puede ignorarla entera y seguir siendo perfectamente conforme al contrato. No es física, es decoración.

Donde más se nota: las cadenas de agentes

Un agente llama a otro, que llama a una herramienta, que devuelve un JSON que alimenta a un tercero. Cuatro saltos. En el salto 1 alguien estaba un 70 % seguro. En el salto 4 esa cifra es un number en un campo, y nadie recuerda —porque no hay dónde recordarlo— si salió de un sensor calibrado, de una base de datos, o de un modelo que la produjo por completar la frase.

El sistema no falla ruidosamente. Falla en verde, con todos los contratos cumplidos y todos los tests pasando. Qué hacer al respecto cuando son varios agentes de inteligencia artificial pasándose trabajo lo desarrollo en sistemas multiagente.

Pruébalo: sácalo de la caverna

Una convención que nada obliga a leer es decoración. Así que aquí tienes una frontera que sí obliga: un ejercicio que no te deja guardar hasta que el JSON diga, sin que nadie tenga que adivinarlo, en qué unidad está cada número, qué fecha es cada fecha, de dónde sale, cuánto se lo cree quien lo envía y hasta cuándo vale.

Fíjate en la primera comprobación. Con el JSON de partida ya está en verde: es la única que miraría un validador. Todas las demás están en rojo, y a él le dan igual.

Ejercicio

Escribe un JSON que una IA no tenga que adivinar

Un agente va a leer este JSON sin documentación, sin Slack y sin nadie a quien preguntar. Reescríbelo para que no tenga que adivinar nada. Sigue siendo JSON corriente: no hace falta ningún formato nuevo, solo convenciones.

Empezar desde…
84 / 4000
Fallan 6 de 7 comprobaciones.

Lo que comprueba un validador JSON

  • Pasa: Está bien formado

    Es lo único que miraría un validador. Todo lo de abajo le da igual.

Lo que necesita un modelo para no adivinar

  • Falla: Ningún número va sin su unidad

    Sin unidad: «importe» (1500). ¿Euros, miligramos, horas? Pon cada número junto a su unidad: { "amount": 1500, "currency": "EUR" } o { "value": 5, "unit": "mg" }.

  • Falla: Ninguna fecha admite dos lecturas

    «fecha» = "01/02/2026": ¿día/mes o mes/día? Un modelo elegirá una de las dos con total aplomo. Usa ISO 8601: "2026-02-01".

  • Falla: Ningún null ambiguo

    «notas» es null. ¿No existe, no se sabe o no aplica? Para un modelo son tres cosas distintas: omite el campo o dilo con un valor explícito (por ejemplo, una lista vacía).

  • Falla: Dice de dónde sale (source)

    Falta "source" en la raíz: el sistema y el registro de donde sale el dato, p. ej. "erp/facturas#8812". Sin eso, un número medido y uno inventado son indistinguibles.

  • Falla: Dice cuánto se lo cree (confidence)

    Falta "confidence" en la raíz: un número entre 0 y 1. Un 1 dice «lo medí»; un 0.6, «lo deduje y puedo equivocarme».

  • Falla: Dice hasta cuándo vale (validUntil)

    Falta "validUntil" en la raíz: el instante (ISO 8601 con zona) a partir del cual el dato deja de ser cierto. La verdad caduca; el campo, no, salvo que se lo digas.

Cuando no falle ninguna comprobación podrás guardarlo como rama.

Cuando pase todo, guárdalo como rama: queda en el historial colectivo del artículo, y cualquiera puede abrirla y forkearla para mejorarla. Ojo con una cosa, que es la trampa de este mismo ejercicio: estas comprobaciones miran que el significado esté dicho, no que sea verdad. Puedes declarar "confidence": 1 sobre algo que te acabas de inventar y el ejercicio no se enterará. Un validador, por listo que sea, sigue dentro de la caverna. Esa objeción la desarrollo entera en En defensa de JSON.

La conclusión incómoda

JSON no está roto. Hace exactamente aquello para lo que se diseñó, y lo hace muy bien: mover estructura de A a B sin opinar sobre su significado. Eso viene de lejos: en 1948 Shannon fundó la teoría de la información diciendo con toda intención que los aspectos semánticos de la comunicación eran irrelevantes para el problema de ingeniería. Tenía razón. Para un cable.

El problema no es el formato. El problema es que montamos encima de él un sistema cognitivo y quitamos la pieza que ponía el significado sin sustituirla por nada.

Salir de la caverna no consiste en validar mejor. Consiste en que el dato lleve encima su tipo ontológico, su certeza, su procedencia y su caducidad, y en que el runtime esté obligado a respetarlos. Eso es exactamente lo que propone ΛD, y lo que ya comprueba el compilador de AXON (axon-lang), el lenguaje de programación que construyo para llevar esa idea a código.

Y porque una tesis que nadie ataca es solo un eslogan, escribí también la mejor defensa que se me ocurre de lo contrario.

Para llevarte

  • "JSON válido" contesta una pregunta sintáctica y ninguna semántica.
  • Durante veinte años bastó porque había un humano en cada extremo poniendo el significado.
  • Ese humano ya no está, y el modelo que lo sustituye rellena el hueco adivinando.
  • Los esquemas afinan el contorno de la sombra. No la convierten en el objeto.