Паттерны для имён свойств

В Ajv имена свойств объекта можно валидировать с помощью регулярных выражений, используя механизмы patternProperties и propertyNames, которые позволяют задавать ограничения не на фиксированные ключи, а на их структуру и соответствие шаблону.

Механизм patternProperties позволяет описывать правила для групп свойств, имена которых соответствуют регулярному выражению. Это особенно полезно в случаях, когда структура объекта динамическая, но подчиняется определённому соглашению об именовании.

const schema = {
  type: "object",
  patternProperties: {
    "^user_[a-z]+$": {
      type: "string"
    }
  }
};

В данном случае любые свойства, начинающиеся с user_ и содержащие только строчные латинские буквы после подчёркивания, должны иметь строковое значение. Ключевым моментом является то, что регулярное выражение применяется именно к имени свойства, а не к его значению.

При этом свойства, не соответствующие ни одному шаблону, могут быть либо разрешены, либо запрещены с помощью additionalProperties.

const schema = {
  type: "object",
  patternProperties: {
    "^user_[a-z]+$": { type: "string" }
  },
  additionalProperties: false
};

Такая комбинация делает структуру объекта строго контролируемой даже при динамических ключах.

propertyNames и контроль допустимых имён

В отличие от patternProperties, конструкция propertyNames применяется ко всем ключам объекта целиком и задаёт единое правило для всех имён свойств.

const schema = {
  type: "object",
  propertyNames: {
    pattern: "^[a-zA-Z0-9_]+$"
  }
};

Здесь любое имя свойства должно соответствовать указанному регулярному выражению. Это означает, что нельзя иметь пробелы, дефисы или специальные символы, если они не включены в шаблон.

propertyNames часто используется как базовый фильтр для нормализации API-ответов или конфигурационных объектов, где требуется единый стиль именования.

Сочетание patternProperties и propertyNames

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

const schema = {
  type: "object",
  propertyNames: {
    pattern: "^[a-z_]+$"
  },
  patternProperties: {
    "^meta_": {
      type: "object"
    },
    "^data_": {
      type: "string"
    }
  }
};

В этом примере сначала проверяется, что все ключи состоят только из строчных букв и подчёркивания, а затем уточняется структура для префиксов meta_ и data_.

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

Поведение при пересечении шаблонов

Если одно свойство соответствует нескольким регулярным выражениям в patternProperties, применяются все совпавшие схемы. Это может привести к конфликтам типов или дополнительных ограничений.

patternProperties: {
  "^a": { type: "string" },
  "^a_b": { type: "number" }
}

Ключ a_b1 соответствует обоим шаблонам, что требует согласования правил. В таких ситуациях важно проектировать регулярные выражения так, чтобы они не пересекались либо имели одинаковую семантику ограничений.

Unicode и особенности регулярных выражений

Ajv использует JavaScript-совместимые регулярные выражения, поэтому поддерживаются Unicode-символы, но поведение зависит от флага u.

const schema = {
  type: "object",
  patternProperties: {
    "^\\p{L}+$": {
      type: "string"
    }
  }
};

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

Производительность при использовании шаблонов

Использование регулярных выражений для имён свойств увеличивает стоимость валидации по сравнению с фиксированными ключами. Особенно это заметно при больших объектах с десятками и сотнями свойств.

Оптимизация достигается за счёт:

  • минимизации количества регулярных выражений;
  • избегания сложных конструкций внутри паттернов;
  • строгого разделения зон ответственности между patternProperties.

Чрезмерно сложные выражения могут существенно замедлить проверку, так как каждое имя свойства тестируется отдельно.

Типовые ошибки при проектировании схем

На практике часто встречаются ошибки, связанные с неправильной интерпретацией шаблонов:

  • перекрывающиеся регулярные выражения, приводящие к конфликтам;
  • отсутствие additionalProperties, из-за чего объект становится слишком свободным;
  • слишком общие паттерны, например ".*", фактически отключающие контроль структуры;
  • использование сложных выражений без необходимости, ухудшающих читаемость и производительность.

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

Практические сценарии применения

Паттерны имён свойств применяются в конфигурациях плагинов, системах плагинной архитектуры, API с расширяемыми полями и локализованных структурах данных.

Типичный пример — хранение переводов:

const schema = {
  type: "object",
  patternProperties: {
    "^[a-z]{2}$": {
      type: "string"
    }
  },
  additionalProperties: false
};

Здесь ключи представляют языковые коды, а значения — строки перевода.

Другой сценарий — метаданные с динамическими ключами:

const schema = {
  type: "object",
  patternProperties: {
    "^x-": {}
  }
};

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

Ограничение области применения шаблонов

Несмотря на гибкость, использование регулярных выражений для имён свойств должно оставаться контролируемым инструментом. В случаях, когда структура данных известна заранее, фиксированные ключи предпочтительнее, так как они обеспечивают лучшую читаемость схемы и более предсказуемую валидацию.

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