API y empresas

Null, campo ausente o cero: tres respuestas distintas en un contrato de infracciones

Un monto cero es un número, null es un valor explícito y un campo ausente no fue enviado. Si el contrato los mezcla, un cliente puede informar que no hay deuda cuando sólo falta un dato.

Por Marco Ferreiro ·

Tres módulos de datos que representan un valor, un nulo y un campo ausente

Una integración recibe "amount": 0 y marca la multa como gratuita. Otra recibe "amount": null y lo convierte en cero al deserializar. Una tercera no recibe amount y conserva el valor anterior. Las tres decisiones pueden ser técnicamente válidas para el lenguaje, pero incorrectas para el negocio.

El contrato de una API de multas debe decir qué significa cada forma y usar el esquema para impedir interpretaciones incompatibles.

JSON distingue valores y presencia

RFC 8259 define null como un valor JSON, al igual que números, cadenas, booleanos, objetos y arrays. Cero es un número. Un miembro ausente, en cambio, no forma parte del objeto recibido.

Estas respuestas no son iguales:

{ "amount": 0 }
{ "amount": null }
{}

JSON Schema también aclara que null no equivale a ausencia. La palabra clave required determina qué propiedades deben estar presentes; type determina qué valores admiten.

Por eso un campo puede ser obligatorio y nullable: siempre aparece, pero a veces contiene null.

Primero definí la pregunta de negocio

Para el importe de un acta, una semántica prudente podría ser:

No alcanza con escribir “puede ser null”. Agregá un campo de razón, por ejemplo amountStatus: "not_reported", y la moneda cuando exista un número. Si cero es válido, documentá qué jurisdicción lo confirmó y evitá usarlo como valor predeterminado.

Un esquema que conserva la diferencia

OpenAPI 3.1 alinea su Schema Object con JSON Schema 2020-12. Un contrato puede exigir la presencia y admitir número o null:

type: object
required: [amount, amountStatus]
properties:
  amount:
    type: [number, 'null']
    minimum: 0
  amountStatus:
    type: string
    enum: [reported, not_reported, not_applicable]

Después agregá reglas de consistencia: reported requiere un número; los otros estados requieren null. JSON Schema puede expresar parte de esas condiciones con if, then y else, o la aplicación puede validarlas al construir la respuesta.

Si el producto necesita omitir el campo en una representación resumida, definí otro esquema o versión. Hacerlo opcional en todas partes obliga a cada cliente a adivinar.

Cero es una afirmación, no un fallback

En multas, amount: 0 puede activar decisiones: no reservar fondos, cerrar un caso o informar que no existe deuda. Usarlo cuando un portal no respondió transforma desconocimiento en certeza.

La misma regla vale para infractionsFound: 0. El cero sólo es interpretable si la consulta terminó con cobertura declarada. Una respuesta debería separar resultados de ejecución:

{
  "infractions": [],
  "coverage": {
    "status": "partial",
    "completedSources": 3,
    "expectedSources": 5
  }
}

La lista vacía describe lo encontrado; partial impide convertirla en “sin multas”.

Null también necesita una razón

Un null sin explicación sólo traslada la ambigüedad. Para dueDate, podría significar que la autoridad no publica vencimiento, que el acta requiere juzgamiento o que la fuente falló. Son situaciones operativas distintas.

Preferí parejas como dueDate y dueDateStatus, con enumeraciones estables. No uses texto libre como única señal porque los clientes terminarán interpretando frases.

Tampoco reutilices null para borrar un valor en una operación PATCH salvo que ese significado esté definido. Leer una respuesta y aplicar un cambio son contratos diferentes.

Cómo reaccionan clientes en distintos lenguajes

Algunos generadores distinguen propiedad opcional de propiedad nullable. Otros mapean ambas a un tipo que puede no tener valor. La prueba debe ocurrir con el código generado, no sólo en una interfaz web.

Construí fixtures para:

Un cliente estricto debe rechazar combinaciones imposibles. Uno tolerante puede conservar campos nuevos, pero no convertir automáticamente valores inválidos.

Compatibilidad al cambiar el contrato

Volver obligatorio un campo antes opcional suele romper clientes. Cambiar null por cero cambia la semántica del importe de un acta aunque el parser acepte ambos. Antes de migrar, versioná el esquema, publicá ejemplos y medí consumidores.

Para una transición, el servidor puede mantener la representación anterior y ofrecer una versión nueva con estados explícitos. No conviene enviar alternativamente ausencia y null según qué jurisdicción respondió: la API de borde debe normalizar la diferencia de manera consistente.

Observabilidad sin reinterpretar datos

Medí conteos por amountStatus, versión de esquema y fuente, sin colocar patentes en etiquetas. Una suba de not_reported puede revelar un cambio de portal; una suba de ceros merece otra alerta. Si ambos se mezclan, la falla queda invisible.

En logs, registrá el error de validación y un identificador opaco de consulta. No vuelques el acta completa como solución de diagnóstico.

La regla que simplifica a todos los clientes

Cada estado debe tener una sola representación canónica. Cero significa cero confirmado por la jurisdicción; null significa el estado explícito documentado; ausencia significa que esa representación no incluye el campo. El esquema valida la forma y los tests validan que dos lenguajes produzcan la misma decisión.

Esa disciplina evita el error más costoso en una API de infracciones: informar una certeza negativa cuando el dato, en realidad, nunca llegó.

Metodología

Alcance
Validación conceptual de fixtures sintéticos con propiedad ausente, null, cero, cadena vacía y lista vacía contra un esquema OpenAPI 3.1.
Unidad de análisis
Una respuesta JSON de infracción y la interpretación que clientes estrictos deben producir para cada combinación permitida.
Cobertura
Modelo de datos JSON, JSON Schema Draft 2020-12 y Schema Object de OpenAPI 3.1.1.

Limitaciones

Fuentes

Preguntas frecuentes

¿Null y campo ausente son equivalentes en JSON Schema?

No. Null es un valor JSON. La ausencia se controla con la lista required del objeto; una propiedad puede admitir null y, al mismo tiempo, ser obligatoria.

¿Una lista vacía significa que se consultaron todas las fuentes y no hubo multas?

Sólo si el contrato lo garantiza y expone la cobertura. Si hubo fuentes pendientes o fallidas, la lista puede estar vacía sin que el resultado sea completo.

¿Conviene usar cero cuando una fuente no informa el monto?

No. Cero es una afirmación numérica y puede activar reglas contables. Usá el estado explícito definido por el contrato y conservá la razón por la cual el importe no está disponible.

Notas relacionadas