В 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
};
Такая комбинация делает структуру объекта строго контролируемой даже при динамических ключах.
В отличие от patternProperties, конструкция
propertyNames применяется ко всем ключам объекта целиком и
задаёт единое правило для всех имён свойств.
const schema = {
type: "object",
propertyNames: {
pattern: "^[a-zA-Z0-9_]+$"
}
};
Здесь любое имя свойства должно соответствовать указанному регулярному выражению. Это означает, что нельзя иметь пробелы, дефисы или специальные символы, если они не включены в шаблон.
propertyNames часто используется как базовый фильтр для
нормализации API-ответов или конфигурационных объектов, где требуется
единый стиль именования.
Одновременное использование обоих механизмов позволяет строить многоуровневую валидацию: один уровень ограничивает общий формат, другой — конкретные группы ключей.
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 соответствует обоим шаблонам, что требует
согласования правил. В таких ситуациях важно проектировать регулярные
выражения так, чтобы они не пересекались либо имели одинаковую семантику
ограничений.
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-": {}
}
};
Такой подход часто используется для пользовательских расширений, где префикс определяет принадлежность к пользовательским полям.
Несмотря на гибкость, использование регулярных выражений для имён свойств должно оставаться контролируемым инструментом. В случаях, когда структура данных известна заранее, фиксированные ключи предпочтительнее, так как они обеспечивают лучшую читаемость схемы и более предсказуемую валидацию.
Шаблонные свойства оправданы только там, где структура действительно динамическая и не может быть выражена конечным набором ключей.