Кортежи и префиксные элементы

Валидация массивов в JSON Schema опирается на два принципиально разных подхода: одинаковая структура элементов и фиксированная позиционная структура. Второй подход реализует концепцию кортежей, где каждый индекс массива имеет строго определённое значение и собственную схему проверки. В библиотеке Ajv этот механизм поддерживается в полном объёме и особенно важен при работе с API, контрактами данных и строгими типами структур.

Кортежная модель массива отличается от обычной тем, что элементы не являются однородными. Каждый индекс рассматривается как отдельное поле с собственной схемой.

Пример концепции:

  • элемент 0 — строка
  • элемент 1 — число
  • элемент 2 — булево значение

Такой массив уже не является коллекцией однотипных значений, а превращается в фиксированную структуру данных, аналогичную структурам или записям в других языках программирования.

В Ajv это используется для строгой валидации входящих данных, где порядок элементов имеет семантическое значение.

Использование items в режиме кортежа (устаревающий подход)

В более ранних версиях JSON Schema (draft-07 и ранее) кортежи описывались через массив схем внутри свойства items:

const schema = {
  type: "array",
  items: [
    { type: "string" },
    { type: "number" },
    { type: "boolean" }
  ]
};

Поведение такой схемы:

  • первый элемент проверяется как строка
  • второй как число
  • третий как boolean
  • все последующие элементы регулируются дополнительными правилами

Если дополнительная логика не указана, то поведение зависит от additionalItems.

Ограничение дополнительных элементов

Для кортежей важно контролировать поведение элементов за пределами определённой длины. В старой модели используется additionalItems:

const schema = {
  type: "array",
  items: [
    { type: "string" },
    { type: "number" }
  ],
  additionalItems: false
};

В этом случае массив обязан содержать ровно два элемента. Любой третий элемент приводит к ошибке валидации.

Также возможен вариант с собственной схемой для дополнительных значений:

additionalItems: { type: "string" }

Тогда все элементы после фиксированной части должны соответствовать указанной схеме.

Современный подход: prefixItems

В спецификации JSON Schema 2020-12 поведение кортежей формализовано через ключ prefixItems. Он отделяет фиксированную часть массива от остальных элементов.

const schema = {
  type: "array",
  prefixItems: [
    { type: "string" },
    { type: "number" },
    { type: "boolean" }
  ]
};

Семантика:

  • первые три элемента строго соответствуют схемам в prefixItems
  • дальнейшие элементы регулируются отдельно через items

Совместное использование prefixItems и items

В новой модели items больше не используется для описания кортежа, а отвечает за хвостовую часть массива:

const schema = {
  type: "array",
  prefixItems: [
    { type: "string" },
    { type: "number" }
  ],
  items: { type: "string" }
};

Поведение:

  • индекс 0 — строка
  • индекс 1 — число
  • все последующие элементы — строки

Такой подход делает структуру более читаемой и явно разделяет фиксированную и повторяющуюся части массива.

Поведение Ajv при валидации кортежей

Ajv интерпретирует схемы в зависимости от версии спецификации. При включённой поддержке draft-2020-12 используется prefixItems, при более старых версиях — массив items.

Внутренний механизм валидации:

  1. Проверка каждого элемента фиксированной части
  2. Сопоставление индекса с соответствующей схемой
  3. Применение правил для остаточных элементов
  4. Проверка ограничений длины массива при наличии additionalItems: false

Ошибки формируются с указанием индекса, что критично для диагностики:

  • data[1] should be number
  • data[2] should be boolean

Частичные кортежи и необязательные элементы

Кортежи могут содержать неопределённые позиции. Это достигается через null, anyOf или oneOf:

prefixItems: [
  { type: "string" },
  { type: ["number", "null"] },
  { type: "boolean" }
]

Такая конструкция позволяет моделировать частично заполненные структуры, сохраняя позиционную строгость.

Сложные кортежные структуры

Кортежи часто используются для описания протокольных данных:

const schema = {
  type: "array",
  prefixItems: [
    { const: "event" },
    { type: "string" },
    { type: "integer" },
    { type: "object" }
  ],
  items: false
};

Здесь:

  • первый элемент фиксирует тип записи
  • второй — идентификатор
  • третий — временная метка
  • четвёртый — payload

Отключение items: false запрещает любые дополнительные данные, превращая массив в строго фиксированную запись.

Отличие кортежей от обычных массивов

Ключевое различие заключается в уровне абстракции:

Обычные массивы:

  • элементы однотипны
  • порядок не имеет значения
  • длина произвольна

Кортежи:

  • каждый индекс имеет собственную семантику
  • порядок критически важен
  • длина часто фиксирована или ограничена

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

Производительность валидации

Кортежи обрабатываются быстрее в сравнении с массивами с комплексными условиями oneOf на каждый элемент, поскольку:

  • схема компилируется по индексам
  • отсутствует перебор вариантов для однородных элементов
  • структура проверки линейная и предсказуемая

Особенно это заметно в больших JSON-документах с повторяющимися структурами.

Типичные ошибки при работе с prefixItems

Часто встречаются следующие проблемы:

  • использование items вместо prefixItems в новых схемах
  • забытый запрет items, приводящий к неожиданным дополнительным элементам
  • несоответствие длины массива ожиданиям схемы
  • смешение старого и нового синтаксиса в одной схеме

Ajv строго разделяет поведение в зависимости от версии спецификации, поэтому некорректная комбинация ключей приводит к непредсказуемой валидации.

Комбинация с валидацией объектов внутри кортежей

Кортежи часто содержат сложные структуры:

prefixItems: [
  { type: "string" },
  {
    type: "object",
    properties: {
      id: { type: "number" },
      name: { type: "string" }
    },
    required: ["id"]
  }
]

Такая схема моделирует компактные протоколы обмена данными, где массив заменяет JSON-объект для уменьшения размера и ускорения парсинга.

Использование в прикладных сценариях

Кортежные схемы особенно характерны для:

  • бинарных или компактных протоколов поверх JSON
  • логов событий
  • сериализации сообщений очередей
  • внутренних RPC-интерфейсов
  • потоковых данных с фиксированными полями

Ajv обеспечивает детерминированную проверку таких структур без необходимости дополнительных трансформаций данных.