Как проверить схему JSON на основе комбинации массива перечислений? - PullRequest
2 голосов
/ 14 июля 2020

Дано:

Скажите, что я определяю схему для контактов. Но у меня может быть «Первичный контакт», «Студент» или тот, кто является обоими; и различные свойства, которые go со всеми тремя вариантами. Типы контактов определены в массиве contact_type: [ "Primary Contact", "Student" ], который может быть как одним, так и обоими.

Скажите, что поля соответствуют типу контакта:

  • Если основной контакт , тогда мне нужен phone_number
  • Если Student, то мне нужно first_name
  • Если Student и основное контактное лицо, то мне нужны phone_number и first_name

Usage

Я использую библиотеку Ajv для проверки в Node.js, используя такой код:

function validator(json_schema){
    const Ajv = require('ajv');
    const ajv = new Ajv({allErrors: true});
    return ajv.compile(json_schema)
}

const validate = validator(json_schema);

const valid = validate(input);

console.log(!!valid); //true or false
console.log(validate.errors)// object or null

Примечание: у меня были проблемы с allErrors: true при использовании anyOf для этого, и Я использую вывод allErrors, чтобы вернуть ВСЕ отсутствующие / недопустимые поля обратно пользователю, а не возвращать проблемы по одной. Ссылка: https://github.com/ajv-validator/ajv/issues/980

Схема

Я написал следующую схему, и она работает, если я использую либо «Студент», либо «Основное контактное лицо», но когда я прохожу оба , он по-прежнему хочет выполнить проверку по ["Студент"] или ["Основное контактное лицо"], а не по обоим.

 {
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",
  "required": [],
  "properties": {},
  "allOf": [
    {
      "if": {
        "properties": {
          "contact_type": {
            "contains": {
              "allOf": [
                {
                  "type": "string",
                  "const": "Primary Contact"
                },
                {
                  "type": "string",
                  "const": "Student"
                }
              ]
            }
          }
        }
      },
      "then": {
        "additionalProperties": false,
        "properties": {
          "contact_type": {
            "type": "array",
            "items": [
              {
                "type": "string",
                "enum": [
                  "Student",
                  "Primary Contact"
                ]
              }
            ]
          },
          "phone": {
            "type": "string"
          },
          "first_name": {
            "type": "string"
          }
        },
        "required": [
          "phone",
          "first_name"
        ]
      }
    },
    {
      "if": {
        "properties": {
          "contact_type": {
            "contains": {
              "type": "string",
              "const": "Student"
            }
          }
        }
      },
      "then": {
        "additionalProperties": false,
        "properties": {
          "contact_type": {
            "type": "array",
            "items": [
              {
                "type": "string",
                "enum": [
                  "Student",
                  "Primary Contact"
                ]
              }
            ]
          },
          "first_name": {
            "type": "string"
          }
        },
        "required": [
          "first_name"
        ]
      }
    },
    {
      "if": {
        "properties": {
          "contact_type": {
            "contains": {
              "type": "string",
              "const": "Primary Contact"
            }
          }
        }
      },
      "then": {
        "additionalProperties": false,
        "properties": {
          "contact_type": {
            "type": "array",
            "items": [
              {
                "type": "string",
                "enum": [
                  "Student",
                  "Primary Contact"
                ]
              }
            ]
          },
          "phone": {
            "type": "string"
          }
        },
        "required": [
          "phone"
        ]
      }
    }
  ]
}

Пример допустимого ввода:

  • Только для ["Основной Контакт "]:
    {
        "contact_type":["Primary Contact"],
        "phone":"something"
    }
  • Только для [« Студент »]:
    {
        "contact_type":["Student"],
        "first_name":"something"
    }
  • Для [« Основное контактное лицо »,» Студент "]
    {
        "contact_type":["Primary Contact", "Student"],
        "phone":"something",
        "first_name":"something"
    }

Вопрос:

Я хотел бы, чтобы это подтвердилось, даже если allErrors: true, возможно ли это? Если нет, как мне изменить схему?

Footnotes

Я не хочу менять "contact_type" на массив, если это не последнее средство. (это требование, но может быть нарушено, только если нет другого пути)

Я не могу допустить никаких дополнительных элементов, поэтому я полностью определяю каждый объект в операторах if, хотя contact_type является обычным явлением. Если я перемещаю contact_type, то получаю сообщения об ошибке о передаче contact_type в качестве дополнительного элемента (он просматривает свойства оператора if и не видит contact_type, когда он переносится в обычное место). Вот почему мой начальный объект properties пуст.

1 Ответ

1 голос
/ 14 июля 2020

Вот как я могу go решить проблему с валидацией: https://jsonschema.dev/s/XLSDB

Вот схема ... (это проще, если вы попытаетесь устранить проблемы)

{
  "$schema": "http://json-schema.org/draft-07/schema",
  "type": "object",

Во-первых, мы хотим определить наши подсхемы условной проверки ...

  "definitions": {
    "is_student": {
      "properties": {
        "contact_type": {
          "contains": {
            "const": "Student"
          }
        }
      }
    },
    "is_primay_contact": {
      "properties": {
        "contact_type": {
          "contains": {
            "const": "Primary Contact"
          }
        }
      }
    }
  },

Далее, я предполагаю, вы всегда хотите contact_type

  "required": ["contact_type"],
  "properties": {
    "contact_type": {
      "type": "array",
      "items": {
        "enum": ["Primary Contact", "Student"]
      }
    },

И нам нужно определить все разрешенные свойства, чтобы предотвратить появление дополнительных свойств. (draft-07 не может «видеть насквозь» ключевые слова аппликатора, такие как allOf. Можно с черновиком 2019-09 и выше, но это уже другая история)

    "phone": true,
    "first_name": true
  },
  "additionalProperties": false,

Теперь нам нужно определить наши структурные ограничения. ..

  "allOf": [
    {

Если контакт является студентом, имя необходимо.

      "if": { "$ref": "#/definitions/is_student" },
      "then": { "required": ["first_name"] }
    },
    {

Если контакт является основным контактным лицом, то требуется телефон.

      "if": { "$ref": "#/definitions/is_primay_contact" },
      "then": { "required": ["phone"] }
    },
    {

Однако, кроме того, если контакт является одновременно студентом и основным контактом ...

      "if": {
        "allOf": [
          { "$ref": "#/definitions/is_student" },
          { "$ref": "#/definitions/is_primay_contact" }
        ]
      },

Тогда нам потребуется и телефон, и имя ...

      "then": {
        "required": ["phone", "first_name"]
      },

В противном случае можно указать телефон или имя (какое из них рассматривается в предыдущем разделе)

      "else": {
        "oneOf": [
          {
            "required": ["phone"]
          },
          {
            "required": ["first_name"]
          }
        ]
      }
    }
  ]
 }

Я не уверен, что это самый чистый подход, но он работает для ваших требований ' ve предоставлено.

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

Сказав это, ajv предоставляет расширение для добавления пользовательских сообщений об ошибках, которые, учитывая способ, которым я разбил проверку на проблемы, можно использовать для добавления пользовательских ошибок, как вы хотите (https://github.com/ajv-validator/ajv-errors).

...