JSON Schema
JSON Schema
JSON Schema — это язык описания структуры JSON-документа, записанный сам на JSON. Схема отвечает на один вопрос: «подходит ли вот этот конкретный документ под наши правила — какие поля обязательны, какого они типа, в каких пределах значения». Это не формат данных и не база — это проверяющий, которого можно передать по сети.
История
Сначала был JSON. Дуглас Крокфорд (Douglas Crockford) в начале 2000-х вытащил из JavaScript подмножество синтаксиса объектов и объявил его форматом обмена данными — сайт json.org появился в 2002 году, первый стандарт RFC 4627 вышел в 2006-м. Формат победил XML в вебе примерно за пять лет по простой причине: он читался человеком и парсился браузером в одну строку.
И тут же вылезла дыра. У XML был XML Schema (XSD, стандарт W3C с 2001 года) и до него DTD — способ формально сказать «в этом документе элемент <order> обязан содержать <customer_id> целым числом». У JSON не было ничего. Каждый разработчик проверял входящие данные руками: if not isinstance(x, int): raise. Пятнадцать проверок на каждый эндпоинт, все написаны по-разному, все расползаются.
Кто и когда придумал. Первые черновики JSON Schema выпустил Крис Зип (Kris Zyp) — американский разработчик, известный по JavaScript-фреймворку Dojo Toolkit и работе в компании SitePen. Он опубликовал документ как Internet-Draft в IETF примерно в 2009–2010 годах (draft-zyp-json-schema-01 датирован 2010-м). Идея была нарочито рекурсивной: раз мы описываем JSON — давайте и описание тоже сделаем JSON-ом. Тогда схему можно хранить в той же базе, гонять по тому же HTTP и парсить теми же библиотеками, что и сами данные.
Вехи:
- 2010, draft-03 — первая версия, которую реально начали использовать.
- 2013, draft-04 — самая долгоживущая. Многие корпоративные системы сидят на ней до сих пор, спустя больше десяти лет. Здесь появились
oneOf/anyOf/allOf— логические комбинаторы. - 2016–2018, draft-06 и draft-07 — наведение порядка:
const,if/then/else, разделение проверок и подсказок. - 2019-09 и 2020-12 — нумерация сменилась с порядковых номеров на даты. Версия 2020-12 сегодня самая распространённая: именно её требуют OpenAPI 3.1 и большинство современных инструментов.
- декабрь 2021 — проект вошёл в OpenJS Foundation (фонд, под которым живут Node.js, jQuery, webpack) как инкубируемый. У спецификации появился формальный дом.
Текущий статус — важная тонкость. JSON Schema, несмотря на двадцатилетнюю историю и промышленное применение, формально так и не стала RFC. Она до сих пор существует в виде Internet-Draft — черновика IETF. При этом её поддерживают Kubernetes, OpenAPI, VS Code, Anthropic и OpenAI. Классический случай, когда де-факто стандарт обогнал де-юре стандарт лет на десять. Владельца в коммерческом смысле нет: спецификация открытая, разработка идёт на GitHub в организации json-schema-org.
Что это такое
Схема — это обычный JSON-объект, у которого ключи имеют особый смысл для валидатора. Вот схема для той самой задачи, из которой выросла эта статья, — записи в журнал проектов:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"skip": { "type": "boolean" },
"title": { "type": "string", "maxLength": 70 },
"what": { "type": "string", "maxLength": 200 },
"decision": { "type": "string", "maxLength": 400 },
"memory": { "type": "array", "items": { "type": "string" } }
},
"required": ["skip", "title", "what"],
"additionalProperties": false
}
Читается почти по-человечески: это объект; у него есть поля такие-то; skip — булев, title — строка не длиннее 70 символов, memory — массив строк; три поля обязательны; ничего сверх перечисленного не пускать.
Дальше берётся валидатор — библиотека, которая принимает два JSON-а (схему и документ) и возвращает «да» или «нет, вот список нарушений». Всё. Схема ничего не делает сама: она не хранит данные, не преобразует их, не исправляет. Она только выносит вердикт.
Чем отличается от похожего:
- JSON Schema vs JSON. JSON — это формат: правила, как расставить скобки и кавычки. Парсер JSON скажет «это валидный JSON» про
{"title": 12345}, потому что синтаксис в порядке. Схема скажет «нет,titleдолжен быть строкой». Синтаксическая корректность и смысловая — два разных этажа. - JSON Schema vs типы в коде (TypeScript, dataclass, struct). Типы живут внутри одной программы и, как правило, стираются при запуске: TypeScript-тип не существует в рантайме, он не проверит данные, прилетевшие по сети. Схема — внешний артефакт, её можно отдать чужой системе на другом языке.
- JSON Schema vs OpenAPI. OpenAPI описывает весь API: адреса, методы, коды ответов, авторизацию. Формы тел запросов и ответов внутри OpenAPI описываются именно JSON Schema. Начиная с OpenAPI 3.1 это уже честная JSON Schema 2020-12, до этого был слегка изменённый диалект — и несовместимость мучила всех.
- JSON Schema vs Protobuf/Avro. Те тоже описывают структуру, но у них своя грамматика (не JSON) и, главное, они завязаны на бинарную сериализацию. Схема Protobuf нужна, чтобы вообще прочитать байты. JSON Schema не нужна, чтобы прочитать документ, — только чтобы его одобрить.
Аналогии из жизни
1. Бланк в МФЦ. Схема — это бланк заявления: вот поля, вот звёздочки у обязательных, вот «дата в формате ДД.ММ.ГГГГ», вот «ИНН — 12 цифр». Приёмщик смотрит и заворачивает неправильно заполненное, не читая сути.
Где ломается: бланк и проверка — это один физический лист бумаги, они неразделимы. JSON Schema — только проверка, отдельно от документа. Данные существуют сами по себе и прекрасно живут без схемы; схема — это второй, необязательный предмет, который надо кому-то не забыть запустить. И ещё: бланк не даст физически написать 15 цифр в 12 клеточках, а схема ничего не запрещает заранее — она ругается уже постфактум.
2. ГОСТ на молоко. Стандарт говорит: жирность 2,5–3,2 %, объём 900–1000 мл, на этикетке обязателен состав. Лаборатория берёт пакет и проверяет по списку.
Где ломается: ГОСТ описывает ещё и процесс — как пастеризовать, при какой температуре везти. JSON Schema про процесс не знает вообще ничего, только про итоговый пакет. И ГОСТ обязателен по закону: нарушил — штраф. Схема не имеет никакой силы, пока конкретный программист не встроил валидатор в конкретное место кода. Схема, лежащая в репозитории и никем не вызываемая, — просто текстовый файл.
3. Досмотр в аэропорту. Проверка на входе, до попадания внутрь периметра: не пронесёшь больше 100 мл жидкости, ножницы длиннее определённой — в багаж. Отсекаем на границе, чтобы дальше в самолёте было спокойно.
Где ломается: досмотр ищет опасное содержимое, а схема — только форму. Строка "'; DROP TABLE users; --" — прекрасная валидная строка длиной меньше 70 символов, схема пропустит её не поморщившись. Проверка формы никогда не заменяет проверку смысла и не заменяет защиту от злого умысла.
Схема — не защита от атак
Валидная по схеме строка может содержать что угодно: SQL-инъекцию, скрипт, чужие персональные данные. JSON Schema отвечает на вопрос «правильной ли формы данные», а не «безопасны ли они». Санитизация, экранирование и права доступа — отдельная работа, схема её не делает.
Как это работает
Пошагово, что происходит в момент проверки.
Шаг 1. Валидатор получает два дерева. Документ (instance) и схему. Дальше он идёт по ним синхронно, как два курсора.
Шаг 2. Ключевые слова делятся на три сорта. Это главное, что стоит понять про устройство схемы:
- Утверждения (assertions) — то, что реально может провалить проверку:
type,required,minimum,maxLength,pattern(регулярное выражение),enum,const. - Применители (applicators) — те, что не проверяют сами, а перенаправляют проверку вглубь:
properties(проверь вот этой подсхемой полеtitle),items(проверь каждый элемент массива),allOf/anyOf/oneOf/not(комбинируй результаты подсхем логически). - Аннотации (annotations) — чистые подсказки, никогда не приводящие к ошибке:
title,description,default,examples. Их читают редакторы и генераторы документации.
Шаг 3. Открытость по умолчанию. Тут почти все обжигаются один раз.
Схема ничего не запрещает, пока ты не запретил явно
properties — это не список разрешённых полей. Это список полей, к которым применяются правила, ЕСЛИ они присутствуют. Документ {"title": "ок", "случайный_мусор": 42} пройдёт проверку. Чтобы отсечь лишнее, нужен "additionalProperties": false. Чтобы поле стало обязательным — required. Логика JSON Schema разрешительная: всё, что не описано, — можно.
Шаг 4. Ссылки и переиспользование. Повторять один и тот же кусок в схеме не надо: определяешь его один раз в $defs и ссылаешься через $ref, синтаксисом JSON Pointer (RFC 6901):
{
"$defs": {
"short_text": { "type": "string", "maxLength": 200 }
},
"properties": {
"what": { "$ref": "#/$defs/short_text" },
"tail": { "$ref": "#/$defs/short_text" }
}
}
$ref умеет ссылаться и на внешний URL — то есть схема может собираться из нескольких файлов, лежащих на разных серверах. Отсюда же берётся возможность рекурсии: схема комментария, внутри которой список ответов той же схемы.
Шаг 5. Вердикт. На выходе — булев результат плюс список ошибок с указателями на конкретные места: /properties/title/maxLength, «строка длиной 94 при максимуме 70».
Шаг 6 (в продакшене). Компиляция. Наивный валидатор каждый раз заново обходит схему — медленно. Промышленные библиотеки (самый известный пример — Ajv, «Another JSON Validator», написанный Евгением Поберезкиным) при первом чтении компилируют схему в код: превращают дерево правил в развёрнутую функцию на JavaScript. Дальше проверка каждого документа — это вызов обычной скомпилированной функции, а не обход дерева. Разница на потоке — порядки.
Отдельный современный сюжет — LLM. Когда языковой модели говорят «верни строго JSON по этой схеме», у продвинутых реализаций работает не уговор, а механика: схема превращается в грамматику, и на каждом шаге генерации маскируются все токены, которые сделали бы документ невалидным. Модель физически не может выдать неправильную скобку. Это называется constrained decoding (генерация с ограничениями). У Anthropic описания инструментов (input_schema) — это JSON Schema; у OpenAI режим Structured Outputs принимает строгое подмножество JSON Schema. То есть схема из инструмента валидации превратилась ещё и в инструмент управления генерацией.
Где встречается в обычной жизни
- Форма на Госуслугах или в банке, которая красным подчёркивает поле: «СНИЛС — 11 цифр», «дата не может быть в будущем». Frontend-часть часто описана именно схемой, а библиотеки вроде
react-jsonschema-formвообще рисуют форму из схемы автоматически — поля, подписи, валидацию. - Редактор кода подчёркивает опечатку в конфиге. Открываешь
package.jsonилиtsconfig.jsonв VS Code, пишешь"strickt": true— редактор жалуется. Это не магия про конкретный файл: редактор по имени файла нашёл схему в публичном каталоге SchemaStore (schemastore.org, больше тысячи схем для распространённых конфигов) и приложил её. - Автодополнение в YAML-файле GitHub Actions. Пишешь workflow, редактор подсказывает
runs-onи список допустимых значений. YAML укладывается в ту же модель данных, что и JSON, поэтому проверяется теми же схемами. - Приложение не принимает настройку. Меняешь параметр в конфиге умного дома, сервиса, игры — и получаешь «недопустимое значение» ещё до сохранения.
- Ошибка 400 Bad Request при работе с любым современным API. В половине случаев за ней стоит валидатор схемы, отвергший тело запроса на самом входе, ещё до бизнес-логики.
Где встречается в IT и бизнесе
- Контракт между командами. Схема — это письменная договорённость «что мы друг другу шлём», которую можно проверить автоматически. Не «Коля сказал, что там всегда есть поле
client_id», а файл в репозитории и падающий тест, если поля нет. Для директора по развитию это ровно тот артефакт, который переживает увольнение Коли. - Проверка конфигов в CI до деплоя. Раскладка окружения, фича-флаги, тарифы, правила скидок — всё, что лежит в JSON/YAML и правится людьми. Схема ловит опечатку на этапе pull request, а не в проде в пятницу вечером.
- Схемы событий в очередях. В Confluent Schema Registry (реестр схем для Apache Kafka) JSON Schema поддерживается наравне с Avro и Protobuf начиная с версии 5.5 (2020 год). Продюсер не может отправить событие, не соответствующее зарегистрированной схеме, — грязные данные не попадают в поток вообще.
- Kubernetes. Когда описываешь свой тип ресурса (CustomResourceDefinition), его структура задаётся схемой в диалекте OpenAPI v3 — прямом потомке JSON Schema. Кластер отвергнет неправильный манифест на приёме.
- Работа с LLM в проде. Любой пайплайн вида «модель извлекает структуру из текста и кладёт в базу» упирается в схему: она и задание модели, и приёмка результата. Без неё в базу однажды приедет
"дата": "в прошлый четверг".
Кто пользуется
- OpenAPI/Swagger — де-факто стандарт описания HTTP API, с версии 3.1 полностью на JSON Schema 2020-12. Через него схемы проходят практически в каждой компании, где есть публичное API.
- Kubernetes — все CRD, а это фундамент современной инфраструктуры в тысячах компаний.
- VS Code и JetBrains — встроенная поддержка «из коробки», десятки миллионов разработчиков ежедневно.
- Ajv — по данным npm, счёт загрузок идёт на десятки миллионов в неделю (он тянется транзитивно вместе с webpack, ESLint и половиной фронтенд-мира). Практически невозможно собрать современный JS-проект, не притащив Ajv.
- Anthropic и OpenAI — описания инструментов и структурированный вывод. То есть каждый вызов tool use в Claude — это, по сути, JSON Schema, отправленная модели.
- SchemaStore.org — открытый каталог, куда сообщество складывает схемы для конфигов популярных инструментов. Больше тысячи схем; именно он делает подсказки в редакторе бесплатными для всех.
- В Python экосистеме — библиотека
jsonschema(автор Джулиан Берман, Julian Berman) и Pydantic, который умеет генерировать JSON Schema из объявленных классов; ровно на этом стоит FastAPI, автоматически выдающий OpenAPI-документацию.
Альтернативы и конкуренты
Protocol Buffers (Protobuf, Google, 2008 в открытом доступе).
Плюсы: компактный бинарный формат, строгая типизация, генерация кода на десятке языков, встроенные правила эволюции схемы, отлично живёт с gRPC.
Минусы: данные нечитаемы глазом, без схемы вообще не расшифровать, отдельный язык описания и компилятор в цепочке сборки.
Apache Avro (2009, экосистема Hadoop).
Плюсы: лучшая в классе работа с эволюцией схемы — читатель и писатель могут иметь разные версии, и система сама разрулит; схема хранится вместе с данными; идеален для больших потоков в Kafka.
Минусы: за пределами мира больших данных почти не встречается, порог входа выше.
TypeScript-типы + Zod (или аналоги вроде Valibot).
Плюсы: пишешь схему прямо в коде, типы выводятся автоматически, сообщения об ошибках человеческие, разработчику удобно.
Минусы: живёт только внутри JavaScript/TypeScript, чужой Python-сервис твой Zod-объект не прочитает. Правда, Zod умеет экспортировать в JSON Schema — часто так и делают.
XML Schema (XSD, W3C, 2001).
Плюсы: очень выразителен, зрелые инструменты, до сих пор стандарт в госсекторе, банках и EDI.
Минусы: сам многословный XML, освоение — недели, для JSON-мира неприменим напрямую.
Когда НЕ стоит использовать
- Когда правило не про форму, а про смысл. «Скидка не больше остатка на счёте», «дата окончания позже даты начала при условии, что тариф не годовой». Формально JSON Schema умеет
if/thenи кросс-полевые трюки, но схема быстро превращается в нечитаемое месиво из вложенныхallOf. Бизнес-правила должны жить в коде, где их можно прочитать и покрыть тестами. Схема — про структуру, точка. - Когда сообщение об ошибке важнее самой проверки. Ошибки JSON Schema — заслуженно нелюбимая её часть. При использовании
anyOfвалидатор честно выдаёт ошибки по всем веткам сразу, и пользователь получает простыню из десяти взаимоисключающих претензий вместо «вы забыли указать email». Если проверяются данные, которые вводит живой человек, поверх схемы всё равно придётся строить свой слой человеческих сообщений. - Когда это разовый скрипт на 30 строк для себя. Схема — это второй артефакт, который надо поддерживать синхронно с кодом. Расхождение схемы и реальности хуже, чем отсутствие схемы: ей верят, а она врёт. Заводить её стоит там, где данные пересекают границу между командами, сервисами или компаниями.
С чего начать на практике
Не пытайся описать схемой всё сразу. Возьми один файл, который люди правят руками и который ломает прод при опечатке — конфиг, тарифы, список фича-флагов. Напиши для него схему на 20 строк, повесь проверку в CI. Дальше станет понятно, где схема окупается, а где мешает.
Связанные понятия
- JSON Pointer (RFC 6901) — синтаксис адресации внутри JSON-документа (
/properties/title), на нём стоят$refи сообщения об ошибках. - Контракт данных (data contract) — формализованное соглашение о структуре и смысле данных между поставщиком и потребителем; схема — его техническая часть.
- Эволюция схемы (schema evolution) — правила изменения структуры так, чтобы старые и новые версии данных сосуществовали; ключевая тема в Avro и Kafka.
- Constrained decoding — генерация текста моделью с запретом токенов, нарушающих заданную грамматику или схему; именно так добиваются «модель всегда возвращает валидный JSON».
- IDL (Interface Definition Language) — общее название языков описания интерфейсов и данных, не зависящих от языка программирования: Protobuf, Thrift, Avro IDL, в широком смысле и JSON Schema.
- Валидация против санитизации — проверка «правильно ли» против очистки «сделать безопасным»; первое не заменяет второе.
Литература и источники
- Официальный сайт спецификации — json-schema.org. Там же лежат все версии черновиков.
- «Understanding JSON Schema» — json-schema.org/understanding-json-schema. Лучшая на свете вводная документация по теме: от
typeдо рекурсивных схем, с примерами на каждый ключ. Английский, читается за вечер. - RFC 8259 — актуальный стандарт самого JSON: rfc-editor.org/rfc/rfc8259. Короткий, 16 страниц.
- Мартин Клеппман, «Designing Data-Intensive Applications» (2017, en; русское издание — «Высоконагруженные приложения», 2018). Глава 4 «Кодирование и эволюция» — лучший разбор того, зачем вообще нужны схемы и как они меняются во времени. Читать даже если не пишешь код.
- Документация Ajv — ajv.js.org. Практическая сторона: как схему компилируют и почему это быстро.
- SchemaStore — schemastore.org/json. Каталог готовых схем; полезно посмотреть, как описаны реальные конфиги.
- Обзорная статья в англоязычной Википедии: en.wikipedia.org/wiki/JSON — про сам формат, со ссылками на схему.
Где встретилось у меня
4 сентября батч-прогоном работал генератор записей в журнал проектов: каждая рабочая сессия сжимается моделью в одну короткую запись со строго заданными полями — что произошло, что решили, почему именно так. Задание модели — это, по сути, схема в чистом виде: перечень полей, обязательность, жёсткие лимиты длины по каждому, отдельный булев флаг «пропустить, тут писать не о чем». Ни одна из трёх десятков попыток в тот день не дошла до записи: все упали раньше, на несовместимости версии клиента с запрошенной моделью. Схема осталась незаполненной — что само по себе неплохая иллюстрация к тезису: схема ничего не гарантирует, пока кто-то не довёл до неё данные.
Краткое резюме
- JSON Schema — это JSON, описывающий структуру другого JSON: какие поля есть, какого они типа, в каких пределах. Отдельный артефакт, который можно передать в чужую систему на чужом языке.
- Придумал Крис Зип около 2009–2010 годов, потому что у JSON, в отличие от XML, не было своего XSD. Актуальная версия — 2020-12. Формально спецификация до сих пор черновик IETF, а не RFC, — и это не мешает ей быть повсюду.
- Главная ловушка: схема разрешительная. Всё, что не описано явно, проходит. Обязательность задаётся через
required, запрет лишних полей — черезadditionalProperties: false. - Она проверяет форму, а не смысл и не безопасность. Бизнес-правила и защиту от инъекций схема не делает.
- Практическая ценность — там, где данные пересекают границу между людьми, командами или компаниями: контракты API, конфиги в CI, события в очередях, структурированный вывод языковых моделей.