В JSON Schema комбинирование типов используется для построения сложных правил валидации, когда одного простого ограничения недостаточно. В библиотеке Ajv это реализовано через набор логических операторов схем, позволяющих описывать пересечения, объединения и исключения типов данных.
Основная задача комбинирования — задавать поведение валидатора при
работе с альтернативными структурами данных. Вместо того чтобы
ограничиваться единственным значением свойства type,
используются конструкции anyOf, oneOf,
allOf, not, а также комбинации ссылок
$ref, позволяющие строить переиспользуемые схемы.
Объединение типов через type
В JSON Schema поле type может принимать не только
строку, но и массив значений. Это базовый способ объединения типов без
логических операторов.
{
"type": ["string", "number"]
}
Такая схема допускает строку или число. В Ajv это интерпретируется как простое логическое «ИЛИ» между типами, без дополнительных условий.
Особенность этого подхода заключается в его ограниченной выразительности: невозможно задать дополнительные правила для каждого типа отдельно. Поэтому он используется в простых сценариях, где различие между типами не требует уточнения.
Комбинация через anyOf
Конструкция anyOf задаёт условие, при котором данные
считаются валидными, если хотя бы одна из схем возвращает успех.
{
"anyOf": [
{ "type": "string", "minLength": 3 },
{ "type": "number", "minimum": 10 }
]
}
В данном случае значение может быть либо строкой длиной от трёх символов, либо числом не меньше 10.
Ключевая особенность anyOf заключается в независимости
схем: каждая под-схема проверяется отдельно, и достаточно одного
успешного результата.
В Ajv это часто используется для описания гибких API-ответов, где структура данных может существенно различаться.
Строгое объединение через oneOf
oneOf требует, чтобы данные соответствовали ровно одной
схеме из набора.
{
"oneOf": [
{ "type": "string", "maxLength": 5 },
{ "type": "string", "minLength": 10 }
]
}
Если значение соответствует сразу нескольким схемам, валидация считается неуспешной.
Внутренне Ajv выполняет проверку всех под-схем и подсчитывает количество совпадений. Условие успеха — строго один положительный результат.
oneOf используется в случаях, когда пересечение условий
недопустимо и требуется однозначная классификация данных.
Пересечение через allOf
allOf реализует логическое «И» между схемами. Данные
должны удовлетворять всем указанным условиям одновременно.
{
"allOf": [
{ "type": "number" },
{ "minimum": 0 },
{ "maximum": 100 }
]
}
Здесь число должно быть одновременно числом, не меньше нуля и не больше ста.
В Ajv allOf часто используется для композиции схем,
когда базовая структура расширяется дополнительными ограничениями.
Исключение через not
Конструкция not инвертирует результат проверки
схемы.
{
"not": {
"type": "string"
}
}
Такое правило означает, что значение не может быть строкой.
not редко используется отдельно. Чаще всего он
комбинируется с anyOf или allOf для создания
сложных ограничений, например исключения определённых подтипов.
Комбинирование с $ref
Одним из ключевых механизмов переиспользования в JSON Schema является
$ref. В Ajv он активно применяется для построения модульных
схем.
{
"$ref": "
}
Комбинирование через ссылки позволяет объединять схемы на уровне архитектуры, а не только логики.
Часто используется совместно с allOf:
{
"allOf": [
{ "$ref": "#/definitions/baseUser" },
{
"properties": {
"role": { "const": "admin" }
}
}
]
}
Таким образом создаётся расширение базовой схемы без дублирования описания.
Пересечение типов и расширение объектов
В объектных схемах комбинирование особенно важно, так как позволяет строить наследуемые структуры.
{
"allOf": [
{
"type": "object",
"properties": {
"id": { "type": "string" }
},
"required": ["id"]
},
{
"type": "object",
"properties": {
"createdAt": { "type": "string", "format": "date-time" }
}
}
]
}
Результатом становится объект, содержащий обе группы свойств.
Ajv объединяет такие схемы в единое правило валидации, не создавая промежуточных структур.
Конфликты при комбинировании
При использовании нескольких операторов могут возникать конфликты типов и ограничений. Например:
{
"allOf": [
{ "type": "string" },
{ "type": "number" }
]
}
Такая схема всегда будет невалидной, так как пересечение типов пустое.
Ajv не пытается автоматически разрешать подобные конфликты — результат определяется строго логикой схемы.
Комбинирование с условными конструкциями
Хотя if/then/else формально не относится к логическим
операторам объединения, оно часто используется совместно с ними.
{
"if": {
"type": "number"
},
"then": {
"minimum": 0
},
"else": {
"type": "string"
}
}
Такая структура позволяет динамически переключать набор правил в зависимости от входных данных.
В Ajv это обрабатывается как последовательная проверка: сначала
условие if, затем соответствующая ветка.
Вложенные комбинации
Сложные схемы часто содержат несколько уровней комбинирования:
{
"anyOf": [
{
"allOf": [
{ "type": "string" },
{ "minLength": 3 }
]
},
{
"allOf": [
{ "type": "number" },
{ "minimum": 100 }
]
}
]
}
Здесь сначала определяется выбор между ветками (anyOf),
а внутри каждой ветки применяется пересечение условий
(allOf).
Ajv рекурсивно обходит такие структуры, формируя дерево проверок.
Особенности выполнения в Ajv
При обработке комбинированных схем Ajv оптимизирует валидацию:
allOf может объединяться в одну функцию проверки;oneOf требует полного перебора вариантов;anyOf завершает проверку при первом успехе (если
включены оптимизации).Это делает комбинированные схемы производительными даже при высокой вложенности.
Комбинирование с примитивными ограничениями
Логические операторы часто используются вместе с базовыми ключевыми словами:
typeenumconstformatpatternНапример:
{
"oneOf": [
{ "type": "string", "pattern": "^[a-z]+$" },
{ "type": "string", "enum": ["ADMIN", "USER"] }
]
}
Такая схема различает свободный текст и фиксированные значения в рамках одного поля.
Структурная композиция схем
Комбинирование типов в Ajv фактически формирует декларативную систему логики, где каждая схема становится самостоятельным модулем. Эти модули объединяются через:
$ref;Такой подход позволяет строить масштабируемые модели данных без необходимости императивной проверки в коде.