Как условно запретить свойства на основании наличия других свойств в JSON Схема? - PullRequest
5 голосов
/ 06 апреля 2020

В моей схеме я объявил эти свойства:

"index_name": {
      "type": "string",
      "examples": ["foo-wwen-live", "foo"]
    },
"locale": {
      "type": "string",
      "examples": ["wwen", "usen", "frfr"]
},
"environment": {
      "type": "string",
      "default": "live",
      "examples": [
        "staging",
        "edgengram",
        "test"
      ]
}

Я хочу, чтобы тело JSON, проверенное по моей схеме, было действительным , только если :

  • index_name присутствует присутствует, а оба locale и environment отсутствуют присутствует;
  • locale и / или enviroment присутствует присутствует, а index_name отсутствует присутствует

Короче говоря, locale и environment никогда не следует смешивать с index_name.

Контрольные примеры и желаемые результаты:

Они должны пройти:
Дело № 1

{
  "locale": "usen"
}

Дело № 2

{
  "environment": "foo"
}

Дело № 3

{
  "environment": "foo",
  "locale": "usen"
}

Дело № 4

{
  "index_name": "foo-usen"
}

Это НЕ должно пройти:
Дело № 5

{
  "index_name": "foo-usen",
  "locale": "usen"
}

Дело № 6

{
  "index_name": "foo-usen",
  "environment": "foo"
}

Дело № 7

{
  "index_name": "foo-usen",
  "locale": "usen",
  "environment": "foo"
}

Я создал следующее правило для своей схемы, однако оно не охватывает все случаи , Например, если присутствуют и locale, и environment, проверка возвращает ошибку, если также присутствует index_name, что является правильным поведением в соответствии со случаем # 7. Но если присутствует только один из locale и environment, он также позволяет присутствовать index_name (ошибка в случаях № 5 и № 6).

  "oneOf": [
    {
      "required": ["index_name"],
      "not": {"required":  ["locale", "environment"]}
    },
    {
      "anyOf": [
        {
          "required": ["locale"],
          "not": {"required": ["index_name"]}
        },
        {
          "required": ["environment"],
          "not": {"required": ["index_name"]}
        }
      ]
    }
  ]

Я получаю смешанную информацию о том, как работает "not": {"required": []} объявление. Некоторые люди утверждают, что это означает, что оно запрещает присутствие чего-либо, объявленного в массиве, в отличие от идеи, которую дает синтаксис. Другое утверждение о том, что это должно быть принято именно так, как звучит: перечисленные в массиве свойства не обязательны - они могут присутствовать, но это не имеет значения, если это не так.

Помимо этого правила, мне также требуется, чтобы во всех случаях присутствовало одно несвязанное свойство, и я установил "additionalProperties": false.

Какое правило удовлетворяет всем моим тестовым примерам?

Ответы [ 2 ]

5 голосов
/ 06 апреля 2020

Зависимости

Это задание для ключевого слова dependencies. Следующее говорит:

  • , если присутствует "locale", то "index_name" запрещено.
  • , если присутствует "environment", тогда "index_name" запрещено.

|

"dependencies": {
  "locale": { "not": { "required": ["index_name"] } },
  "environment": { "not": { "required": ["index_name"] } }
}

Что случилось с not - required?

Есть дополнительный вопрос о том, как not - required работает. Это сбивает с толку, потому что это не означает, как это читается в Engli sh, но это достаточно похоже, чтобы заставить нас думать, что иногда это так.

В приведенном выше примере, если мы читаем это как «не требуется», Похоже, это означает «необязательно». Более точное описание будет «запрещено».

Это неловко, но не так уж плохо. Это сбивает с толку, когда вы хотите «запретить» более одного свойства. Предположим, мы хотим сказать, что если присутствует «foo», то «bar» и «baz» запрещены. Первое, что вы можете попробовать, это:

"dependencies": {
  "foo": { "not": { "required": ["bar", "baz"] } }
}

Однако, это говорит о том, что если присутствует «foo», то экземпляр недействителен, если оба «bar» И » Баз "присутствуют. Они оба должны быть там, чтобы вызвать провал. Что мы действительно хотели, так это чтобы оно было недействительным, если присутствует «bar» ИЛИ «baz».

"dependencies": {
  "foo": {
    "not": {
      "anyOf": [
        { "required": ["bar"] },
        { "required": ["baz"] }
      ]
    }
  }
}

Почему это так сложно?

JSON Схема оптимизирована для схем, которые допускают изменения. Схема должна обеспечивать, чтобы экземпляр имел необходимые данные для выполнения sh определенной задачи. Если его больше, чем нужно, приложение игнорирует все остальное. Таким образом, если что-то добавить к экземпляру, все по-прежнему работает. Проверка не должна пройти проверку, если у экземпляра есть несколько дополнительных полей, которые приложение не использует.

Итак, когда вы пытаетесь сделать что-то вроде запрета вещей, которые вы могли бы игнорировать, вы получаете немного против зерна JSON Схема и все может стать немного уродливым. Однако иногда это необходимо. Я не знаю достаточно о вашей ситуации, чтобы сделать этот звонок, но я бы предположил, что dependencies, вероятно, необходимо в этом случае, но additionalProperties не является.

1 голос
/ 06 апреля 2020

, поскольку required: [a, b] означает (должен присутствовать И должен присутствовать b)

, тогда not: {required: [a, b]} означает (НЕ (должен присутствовать И должен присутствовать b))

который логически эквивалентен (a не должен присутствовать ИЛИ b не должен присутствовать).

, так что это неправильное выражение, чтобы сказать, что (a не должно присутствовать, а b не должно присутствовать). вам нужно два not с.

вот правильное выражение, учитывая ваши требования:

{
  "oneOf": [
    {
      "required": ["index_name"],
      "allOf": [
        {"not": {"required": ["locale"]}},
        {"not": {"required": ["environment"]}}
      ]
    },
    {
      "anyOf": [
        {"required": ["locale"]},
        {"required": ["environment"]}
      ],
      "not": {
        "required": ["index_name"]
      }
    }
  ]
}
...