Валидация условной схемы ajv на основе данных

Я хотел бы указать шаблон регулярного выражения для одного поля на основе данных в другом. Это возможно? Я пробовал switch и $data, но не уверен, как их использовать. например, если данные выглядят так:

{
   "contacts":[
      {
         "mode":"Email",
         "contact":"john.doe@abc.com"
      },
      {
         "mode":"Phone",
         "contact":"111-555-1234"
      }
   ]
}

и схема выглядит примерно так:

"$schema":"http://json-schema.org/draft-04/schema#",
   "type":"object",
   "properties":{
      "Contacts":{
         "type":"array",
         "minItems":1,
         "items":{
            "type":"object",
            "properties":{
               "mode":{
                  "type":"string",
                  "enum":[
                     "Email",
                     "Phone"
                  ]
               },
               "contact":{
                  "type":"string",
                  "pattern":"?????"
               }
            },
            "required":[
               "mode",
               "contact"
            ]
         }
      }
   }
}

Как я могу установить шаблон контакта на основе данных в режиме, чтобы в режиме "Электронная почта" он проверял контакт по регулярному выражению для формата электронной почты, а в режиме "Телефон" проверял контакт по регулярному выражению для формата телефона? У меня есть регулярное выражение для каждого. Мне нужна логика, чтобы выбрать один или другой.

1 ответ

Есть несколько способов сделать это

anyOf (плюсы: совместимость с draft-04, минусы: отчеты об ошибках немного подробны - вы получите ошибки от обеих подсхем, если ни одна не совпадает):

{
   "type": "object",
   "properties": {
      "Contacts": {
         "type": "array",
         "minItems": 1,
         "items": {
            "type": "object",
            "anyOf": [
               {
                  "properties": {
                     "mode": {"enum": ["Email"]},
                     "contact": {
                        "type": "string",
                        "format": "email"
                     }
                  }
               },
               {
                  "properties": {
                     "mode": {"enum": ["Phone"]},
                     "contact": {
                        "type": "string",
                        "pattern": "phone_pattern"
                     }
                  }
               }
            ],
            "required": ["mode", "contact"]
         }
      }
   }
}

if / then / else ( доступно в пакете ключевых слов ajv, плюсы: отчеты об ошибках имеют больше смысла, принято для включения в черновик-07, минусы: на данный момент не стандартные):

{
   "type": "object",
   "properties": {
      "Contacts": {
         "type": "array",
         "minItems": 1,
         "items": {
            "type": "object",
            "properties": {
               "mode": {"type": "string", "enum": ["Email", "Phone"]},
               "contact": {"type": "string"}
            },
            "if": {
               "properties": {
                  "mode": {"enum": ["Email"]}
               }
            },
            "then": {
               "properties": {
                  "contact": {"format": "email"}
               }
            },
            "else": {
               "properties": {
                  "contact":  {"pattern": "phone_pattern"}
               }
            }
            "required": ["mode", "contact"]
         }
      }
   }
}

select ( доступно в пакете ajv-Keywords, плюсы: более краткие, чем if/then/else, особенно если имеется более двух возможных значений, минусы: пока не на стандартном треке, но вы можете это поддерживать:), требует включения $ ссылка на данные и Ajv v5.xx):

{
   "type": "object",
   "properties": {
      "Contacts": {
         "type": "array",
         "minItems": 1,
         "items": {
            "type": "object",
            "properties": {
               "mode": {"type": "string"},
               "contact": {"type": "string"}
            },
            "select": { "$data": "0/mode" },
            "selectCases": {
               "Email": {
                  "properties": {
                     "contact": {"format": "email"}
                  }
               },
               "Phone": {
                  "properties": {
                     "contact": {"pattern": "phone_pattern"}
                  }
               }
            },
            "selectDefault": false,
            "required": ["mode", "contact"]
         }
      }
   }
}

Я предпочитаю последний вариант.

Другие вопросы по тегам