Справочник · Данные и форматы
JSON Schema: как описать и проверить структуру JSON
Ключевые слова черновика 2020-12 с примерами: типы, строки и числа, объекты и массивы, ссылки, условия и типичные ошибки в схемах.
JSON Schema — это JSON-документ, который описывает, какими должны быть другие JSON-документы. По схеме проверяют конфиги, тела запросов к API и ответы, по ней же редакторы подсказывают поля, а генераторы делают типы для TypeScript, Go и Python. Ниже — актуальная версия стандарта, черновик 2020-12.
Первая схема
{
"title": "Пользователь",
"type": "object",
"properties": {
"id": { "type": "integer", "minimum": 1 },
"email": { "type": "string", "format": "email" },
"name": { "type": "string", "minLength": 1, "maxLength": 100 },
"roles": {
"type": "array",
"items": { "enum": ["admin", "editor", "viewer"] },
"uniqueItems": true
}
},
"required": ["id", "email"],
"additionalProperties": false
}Этому документу схема соответствует:
{ "id": 7, "email": "olga@example.com", "roles": ["editor"] }А этому нет: id строкой, неизвестное поле age и повтор в roles:
{ "id": "7", "email": "olga@example.com", "age": 30, "roles": ["viewer", "viewer"] }Типы
type | Что подходит | Примечание |
|---|---|---|
"string" | строка | |
"number" | любое число | |
"integer" | число без дробной части | 1.0 тоже считается целым |
"boolean" | true, false | |
"null" | null | |
"object" | объект | |
"array" | массив | |
["string", "null"] | строка или null | так описывают необязательные значения |
Схема без type разрешает любой тип. Пустая схема {} и true принимают всё, false — ничего.
Строки и числа
| Ключ | Пример | Смысл |
|---|---|---|
minLength, maxLength | "maxLength": 64 | длина в символах Юникода, а не в байтах |
pattern | "pattern": "^[a-z0-9-]+$" | регулярное выражение по правилам ECMA-262 |
format | "format": "date-time" | date, time, email, uri, uuid, ipv4, ipv6, hostname и другие |
enum | "enum": ["low", "high"] | значение из списка |
const | "const": "v2" | ровно это значение |
minimum, maximum | "minimum": 0 | границы включительно |
exclusiveMinimum, exclusiveMaximum | "exclusiveMaximum": 100 | границы не включаются |
multipleOf | "multipleOf": 0.01 | кратность, например копейки |
Объекты
| Ключ | Что задаёт |
|---|---|
properties | схемы для известных полей |
required | список обязательных полей |
additionalProperties | схема или false для всех полей, не упомянутых в properties и patternProperties |
patternProperties | схемы для полей, чьё имя подходит под регулярное выражение |
propertyNames | ограничение на сами имена полей |
minProperties, maxProperties | число полей |
dependentRequired | если есть поле A, обязательны B и C |
dependentSchemas | если есть поле A, весь объект должен подходить под схему |
unevaluatedProperties | как additionalProperties, но видит поля из allOf, $ref и условий |
{
"type": "object",
"properties": {
"card": { "type": "string" },
"expiry": { "type": "string", "pattern": "^(0[1-9]|1[0-2])/[0-9]{2}$" }
},
"dependentRequired": { "card": ["expiry"] },
"patternProperties": { "^x-": {} },
"additionalProperties": false
}Здесь поле expiry обязательно, только когда передан card, а любые поля с префиксом x- разрешены без проверки.
Массивы
В 2020-12 ключи для массивов переделали, и старые примеры из интернета часто не работают.
| Ключ | Что задаёт |
|---|---|
items | схема для всех элементов (после prefixItems, если он есть) |
prefixItems | массив схем для элементов по позициям: кортеж |
items: false | запрет на элементы сверх prefixItems |
contains | хотя бы один элемент подходит под схему |
minContains, maxContains | сколько элементов должно подойти под contains |
minItems, maxItems | длина массива |
uniqueItems | все элементы разные |
{
"type": "array",
"prefixItems": [
{ "type": "number", "description": "широта" },
{ "type": "number", "description": "долгота" }
],
"items": false
}Повторное использование: $defs и $ref
{
"$defs": {
"money": {
"type": "object",
"properties": {
"amount": { "type": "integer", "minimum": 0 },
"currency": { "enum": ["RUB", "USD", "EUR"] }
},
"required": ["amount", "currency"]
}
},
"type": "object",
"properties": {
"price": { "$ref": "#/$defs/money" },
"discount": { "$ref": "#/$defs/money" }
}
}Ссылка #/$defs/money — это указатель JSON Pointer внутри того же документа. Ссылаться можно и на другой файл: "$ref": "money.json" разрешается относительно $id текущей схемы. В 2020-12 рядом с $ref можно писать другие ключи, они применяются вместе с ним.
Комбинации и условия
| Ключ | Документ подходит, если |
|---|---|
allOf | подходит под все схемы списка |
anyOf | подходит хотя бы под одну |
oneOf | подходит ровно под одну |
not | не подходит под схему |
if / then / else | если подходит под if, проверяется then, иначе else |
{
"type": "object",
"properties": {
"delivery": { "enum": ["courier", "pickup"] },
"address": { "type": "string" },
"point_id": { "type": "integer" }
},
"required": ["delivery"],
"if": { "properties": { "delivery": { "const": "courier" } } },
"then": { "required": ["address"] },
"else": { "required": ["point_id"] }
}Аннотации
Ключи title, description, default, examples, deprecated, readOnly, writeOnly и $comment ничего не проверяют. Их читают редакторы, генераторы документации и форм. Особенно важно помнить про default: валидатор не подставляет значение по умолчанию в документ, это делает ваш код.
Где проверять
- Python: пакет
jsonschema, классDraft202012Validator. - JavaScript: библиотека Ajv, для 2020-12 импортируется отдельный класс
Ajv2020, форматы подключаются пакетомajv-formats. - Go:
santhosh-tekuri/jsonschema. - Редакторы: VS Code и JetBrains подхватывают схему из поля
$schemaв JSON-файле или из настроек проекта и подсказывают поля прямо при наборе.
Сам JSON перед проверкой удобно отформатировать в форматтере, а YAML-конфиг сначала перевести в JSON конвертером: схемы работают и для YAML, потому что модель данных та же.