атрибут jsonSchema требуется условно

104

В jsonSchema вы можете указать, являются ли определенные поля обязательными или нет, используя requiredатрибут:

{
    "$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"
    ]
}

В некоторых случаях я бы хотел, чтобы messageVersionполе не было обязательным. Есть ли способ сделать условной обязательность этого поля?

Том Редферн
источник
Да, это должно быть возможно. Какая информация в данных приведет к обязательности?
jruizaranguren
@SarveswaranMeenakshiSundaram - я не знаю, я использовал только v4 схемы json
том редферн
Возможно ли это вообще в версии 3?
Сарвеш
@SarveswaranMeenakshiSundaram - я не знаю. Попробуйте и дайте нам знать, пожалуйста!
Tom Redfern

Ответы:

277

В зависимости от вашей ситуации есть несколько разных подходов. Я могу придумать четыре разных способа условного требования поля.

Зависимости

dependenciesКлючевым словом является условным изменением requiredключевого слова. Свойство Foreach в dependencies, если свойство присутствует в проверяемом JSON, то схема, связанная с этим ключом, также должна быть действительной. Если свойство "foo" присутствует, то свойство "bar" обязательно.

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

Также существует краткая форма, если схема содержит только requiredключевое слово.

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

Последствия

Если ваше условие зависит от значения поля, вы можете использовать концепцию логической логики, называемую импликацией. «A подразумевает B» эффективно означает, что если A истинно, то B также должно быть истинным. Следствие также может быть выражено как «! A или B». Либо свойство «foo» не равно «bar», либо свойство «bar» является обязательным . Или, другими словами: если свойство "foo" равно "bar", тогда свойство "bar" обязательно.

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

Если «foo» не равно «bar», #/anyOf/0совпадения и проверка пройдены успешно. Если «foo» равно «bar», #/anyOf/0не выполняется и #/anyOf/1должно быть действительным для anyOfуспешной проверки.

Enum

Если ваше условное выражение основано на перечислении, это немного проще. «foo» может быть «bar» или «baz». Если «foo» равно «bar», тогда «bar» является обязательным. Если «foo» равно «baz», то «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"]
    }
  ]
}

Если-то-еще

Относительно новое дополнение к схеме JSON (draft-07) добавляет ключевые слова if, thenи else. Если свойство "foo" равно "bar", тогда свойство "bar" обязательно.

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

РЕДАКТИРОВАТЬ 23.12.2017: обновлен раздел о последствиях и добавлен раздел If-Then-Else.

РЕДАКТИРОВАТЬ 06/04/2018: Исправление ошибки для If-Then-Else и обновление синглтонов enumдля использования const.

Джейсон Дерозье
источник
7
@scubbo Я не поклонник if-then-elseключевых слов и отказываюсь их использовать. Но, если вы решите использовать его, я предлагаю всегда заключать их в тег allOf, содержащий только эти три ключевых слова. { ...other_keywords..., "allOf": [{ "if": ..., "then": ..., "else": ... }], ...more_keywords... }
Джейсон Десрозье
2
@ Джейсон Почему не фанат if...? Я думаю, что краткое мнение по этому поводу в вашем ответе было бы полностью оправдано. Или это долгая история?
Clay Bridges
7
@ClayBridges Раздел комментариев не подходящее место для этого обсуждения, но вот краткая версия. Как правило, ключевые слова схемы JSON не имеют состояния. Никакая информация, кроме значения ключевого слова, не может использоваться для проверки экземпляра. if, thenИ elseнарушать это правило , потому что они зависят друг от друга.
Джейсон Дерозье
3
@GGirard, это лучший способ использования этих шаблонов в схеме JSON, о котором я знаю. Логические операции официально задокументированы, а остальное - просто математика. allOf== И, anyOf== ИЛИ, oneOf== XOR и not== НЕ. Вы можете погуглить "логическую алгебру" для получения дополнительных ресурсов по математике (например, импликации).
Джейсон Дерозье
2
@AlexeyShrub Я давно хотел написать об этом, но меня отвлекли другие вещи. Я поклонник идеи условной. Это действительно облегчает понимание людьми. Я возражаю против того, как это было определено как три отдельных ключевых слова с отслеживанием состояния (см. Предыдущий комментарий). Наличие ключевых слов, которые нарушают архитектурные свойства, которым следуют другие ключевые слова, затрудняет реализацию валидаторов схемы JSON и делает их менее эффективными. Если бы условные выражения были определены иначе, чем без состояния, у меня не было бы возражений.
Джейсон Дерозье