Atributo jsonSchema requerido condicionalmente

97

En jsonSchema puedes indicar si los campos definidos son obligatorios o no usando el requiredatributo:

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "properties": {
        "header": {
            "type": "object",
            "properties": {
                "messageName": {
                    "type": "string"
                },
                "messageVersion": {
                    "type": "string"
                }
            },
            "required": [
                "messageName",
                "messageVersion"
            ]
        }
    },
    "required": [
        "header"
    ]
}

En ciertos casos, me gustaría que el messageVersioncampo no fuera obligatorio. ¿Hay alguna forma de condicionar la obligatoriedad de este campo?

tom redfern
fuente
Sí, debería ser posible. ¿Qué información en los datos activaría la obligatoriedad?
jruizaranguren
@SarveswaranMeenakshiSundaram - No sé, solo he usado v4 del esquema json
tom redfern
¿Es esto posible en absoluto en la versión 3?
Sarvesh
@SarveswaranMeenakshiSundaram - No lo sé. Pruébelo y avísenos por favor!
tom redfern

Respuestas:

264

Dependiendo de su situación, existen algunos enfoques diferentes. Puedo pensar en cuatro formas diferentes de requerir condicionalmente un campo.

Dependencias

La dependenciespalabra clave es una variación condicional de la requiredpalabra clave. Para cada propiedad en dependencies, si la propiedad está presente en el JSON que se está validando, entonces el esquema asociado con esa clave también debe ser válido. Si la propiedad "foo" está presente, se requiere la propiedad "bar"

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" }
  },
  "dependencies": {
    "foo": { "required": ["bar"] }
  }
}

También hay una forma corta si el esquema solo contiene la requiredpalabra clave.

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" }
  },
  "dependencies": {
    "foo": ["bar"]
  }
}

Implicación

Si su condición depende del valor de un campo, puede usar un concepto lógico booleano llamado implicación. "A implica B" significa efectivamente que si A es verdadero, entonces B también debe serlo. La implicación también se puede expresar como "! A o B". O la propiedad "foo" no es igual a "bar", o la propiedad "bar" es obligatoria . O, en otras palabras: si la propiedad "foo" es igual a "bar", entonces se requiere la propiedad "bar"

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" }
  },
  "anyOf": [
    {
      "not": {
        "properties": {
          "foo": { "const": "bar" }
        },
        "required": ["foo"]
      }
    },
    { "required": ["bar"] }
  ]
}

Si "foo" no es igual a "bar", las #/anyOf/0coincidencias y la validación se realizan correctamente. Si "foo" es igual a "bar", #/anyOf/0falla y #/anyOf/1debe ser válido para que la anyOfvalidación sea exitosa.

Enum

Si su condicional se basa en una enumeración, es un poco más sencillo. "foo" puede ser "bar" o "baz". Si "foo" es igual a "bar", entonces se requiere "bar". Si "foo" es igual a "baz", entonces se requiere "baz".

{
  "type": "object",
  "properties": {
    "foo": { "enum": ["bar", "baz"] },
    "bar": { "type": "string" },
    "baz": { "type": "string" }
  },
  "anyOf": [
    {
      "properties": {
        "foo": { "const": "bar" }
      },
      "required": ["bar"]
    },
    {
      "properties": {
        "foo": { "const": "baz" }
      },
      "required": ["baz"]
    }
  ]
}

If-Then-Else

Una adición relativamente nueva a JSON Schema (borrador-07) agrega las palabras clave if, theny else. Si la propiedad "foo" es igual a "bar", entonces se requiere la propiedad "bar"

{
  "type": "object",
  "properties": {
    "foo": { "type": "string" },
    "bar": { "type": "string" }
  },
  "if": {
    "properties": {
      "foo": { "const": "bar" }
    },
    "required": ["foo"]
  },
  "then": { "required": ["bar"] }
}

EDITAR 23/12/2017: Se actualizó la sección Implicación y se agregó la sección If-Then-Else.

EDITAR 04/06/2018: Corrección de errores para If-Then-Else y actualice singleton enums para usar const.

Jason Desrosiers
fuente
7
@scubbo No soy fanático de las if-then-elsepalabras clave y me niego a usarlas. Pero, si eliges usarlo, te sugiero que siempre los envuelvas en un allOfformato que contenga solo esas tres palabras clave. { ...other_keywords..., "allOf": [{ "if": ..., "then": ..., "else": ... }], ...more_keywords... }
Jason Desrosiers
2
@Jason ¿Por qué no eres fan if...? Creo que una breve opinión sobre esto en su respuesta estaría totalmente justificada. ¿O es una larga historia?
Clay Bridges
6
@ClayBridges La sección de comentarios no es el lugar adecuado para esa discusión, pero aquí está la versión corta. Como regla general, las palabras clave de JSON Schema no tienen estado. No se puede utilizar ninguna información que no sea el valor de la palabra clave para validar la instancia. if, theny elseviolan esta regla porque dependen unos de otros.
Jason Desrosiers
3
@GGirard, este es el mejor tratamiento del uso de estos patrones en JSON Schema que conozco. Las operaciones booleanas están documentadas oficialmente, pero el resto son solo matemáticas. allOf== Y, anyOf== O, oneOf== XOR y not== NO. Puede buscar en Google "álgebra booleana" para obtener más recursos sobre cuestiones matemáticas (como la implicación).
Jason Desrosiers
2
@AlexeyShrub He querido escribir sobre esto por un tiempo, pero me han distraído otras cosas. Soy fanático de la idea de un condicional. Hace que sea más fácil de entender para las personas. Mi objeción es la forma en que se definió como tres palabras clave con estado separadas (ver comentario anterior). Tener palabras clave que violan las propiedades arquitectónicas que siguen otras palabras clave hace que los validadores de esquema JSON sean más difíciles de implementar y menos eficientes. Si los condicionales se definieran de una manera diferente que fuera apátrida, entonces no tendría ninguna objeción.
Jason Desrosiers