JSON: форматирование, валидация и JSON Schema в реальной работе

Опубликовано 2026-04-13 8 мин чтения

Резюме (TL;DR)

В прошлом месяце одна команда правила строчку в feature_flags.json и выкатила её в продакшен. JSON.parse прошёл, CI был зелёный, а на staging выяснилось, что flags.checkout_v2 стоит "true" строкой, и все ветки оплаты свалились на старую реализацию. Фраза «я проверил JSON» на самом деле склеивает три разные операции — форматирование, проверку синтаксиса и проверку структуры — и в тот день команда сделала только первые две. Форматирование (pretty-print) просто расставляет пробелы и переносы строк; оно ничего не проверяет. Синтаксическая валидация сверяет скобки, кавычки и escape-последовательности с RFC 8259; именно этим занимается JSON.parse. Структурная валидация приходит потом и отвечает на вопрос «соответствует ли эта форма тому, чего ждёт мой код?» — ради этого и придуман JSON Schema. Как только эти три шага ставят в один флажок «валидация JSON», появляются бесшумные сбои вроде описанного. Хотите прочитать — форматируйте. Принимаете вход — парсите. А на границах доверия — API, конфиги, сообщения в очередях — валидация через Ajv или аналогичный инструмент со схемой обязательна. Пайплайн, где все три шага стоят на своих местах, надёжнее.

Предыстория и концепции

JSON определён в RFC 8259 (и эквивалентном ECMA-404). Грамматика небольшая и простая. Документ — это строка, число, true, false, null, массив или объект. Строки заключены в двойные кавычки и поддерживают escape-последовательности \n, \t, \", \\, \uXXXX (юникод-кодюнит). Числа не делятся на целые и с плавающей точкой и записываются в десятичной записи. Объект — это неупорядоченный набор пар со строковым ключом, массив — упорядоченный список значений. Пробелы вне строк смысла не имеют.

Не менее важно то, что JSON намеренно не включает: комментариев нет, замыкающих запятых нет, одинарных кавычек, шестнадцатеричных и двоичных литералов тоже. Не-ASCII символы пишутся либо как UTF-8 байты, либо через \u-escape. JSON5 и HJSON — это отдельные форматы с ослабленными правилами, и строгие JSON-парсеры их не принимают.

Успешный разбор означает лишь «синтаксически корректно», а не «семантически верно». {"user": "x", "pass": "y"} — безупречный JSON, но если эндпоинт ждал {"username": "...", "password": "..."}, с точки зрения приложения данные некорректны. Чтобы ловить такие вещи, нужна схема — машиночитаемое «описание допустимых форм документа», — и стандарт для этого — JSON Schema (актуальная мета-схема — Draft 2020-12). Он поддерживает обязательные поля, типы, enum и const, шаблоны строк через регулярные выражения, диапазоны чисел, items и уникальность у массивов, properties и additionalProperties у объектов, композицию через allOf/oneOf/anyOf/$ref. Валидаторы вроде Ajv 8.12 компилируют схему в функцию один раз, поэтому в горячем пути проверка почти бесплатна: в одном API, который я сопровождал, скомпилированная Ajv-валидация занимала десятки микросекунд.

Форматирование — самая простая из трёх задач. Оно не трогает значений, только правит пробелы и отступы: JSON.stringify(obj, null, 2), jq ., встроенный форматтер в IDE. Это для людей; машине оно безразлично.

Сравнение и данные

РакурсФорматированиеСинтаксическая валидацияВалидация JSON Schema
ЦельСделать читаемымПроверить, что строка — валидный JSONПроверить, что распарсенное значение имеет ожидаемую форму
Что ловитНичего (только пробелы)Несбалансированные скобки, ошибочные кавычки, плохие escape-последовательности, замыкающие запятыеОтсутствие обязательных полей, несовпадение типов, выход за диапазон, неизвестные ключи
Что не ловитСтруктуру и семантикуФорма документа и бизнес-правилаПравила вне схемы, связи между полями
Типовые инструментыJSON.stringify(obj, null, 2), jq, форматтер IDEJSON.parse, jq -e, парсеры в языкахAjv 8.x, python-jsonschema, OpenAPI-валидаторы
Где применяетсяИнструменты разработчика, логиЛюбая граница парсинга (неявно)Запрос/ответ API, загрузка конфигов, границы сообщений

Эти три колонки — не «выберите одну», а последовательные уровни. Посмотреть на package-lock.json весом 12 МБ и привести его к читаемому виду — это форматирование; стоит назвать это «валидацией», и начинается инцидент. Читаем через форматирование, отсекаем битые строки парсингом, проверяем форму схемой. Типичная дыра — сделать первые два шага и забыть третий.

Практические сценарии

Сценарий 1 — отладка ответа API. Внешний платёжный шлюз возвращает 600 строк JSON в одну строку. Вкладка Network в DevTools, локальный форматтер или конвейер вроде curl ... | jq . быстро приводят его к виду, который можно просмотреть глазами. Здесь нет никакой валидации, цель — «визуально найти, где странно», и важно не называть этот шаг «проверкой».

Сценарий 2 — загрузка конфига. Сервис на старте читает config.json. Строгий JSON-парсер поймает, например, замыкающую запятую и не даст стартовать — это правильное поведение. Но, как в примере из вступления, в retries: 3 может оказаться retries: "three", и парсинг пройдёт; проблема вылезет, только когда код попробует сравнить строку с числом. Паттерн, который я предложил одной команде: на первой строке старта вызывать функцию валидации, полученную через Ajv compile(schema), проверять обязательные поля, типы, диапазоны и enum, а при нарушении делать process.exit(1). Пятиминутное изменение уже дважды предотвращало двухдневные staging-аварии.

Сценарий 3 — контрактные тесты OpenAPI. Команда описывает эндпоинты, тела запросов и формы ответов в OpenAPI 3.1 через components.schemas. Контрактные тесты прогоняют примерные payload-ы из спецификации через ту же схему и ловят дрейф, когда серверная реализация начинает возвращать «целое число там, где должна быть строка», — до того как клиент сломается. Движок тот же самый JSON Schema-валидатор; разница только в масштабе применения.

Распространённые заблуждения

«Достаточно JSON.parse — и всё провалидировано». Он проверяет только синтаксис и не смотрит форму. Парсер спокойно вернёт объект, где половина полей пуста. Относитесь к JSON.parse как к фильтру против битых строк, а на границах доверия кладите сверху ещё слой — валидацию по схеме.

«JSON Schema — это только для сервера». Если проверять запрос в браузере ещё до отправки, пользователь сразу получает обратную связь, а нагрузка на сервер падает. Ajv и ряд других валидаторов отлично работают в браузере. Серверная валидация всё равно обязательна — клиентам доверять нельзя, — но клиентская улучшает UX и не ослабляет модель безопасности.

«JSON5 — это JSON с комментариями». JSON5 добавляет комментарии, ключи без кавычек, замыкающие запятые, шестнадцатеричные литералы и прочее. Как формат для ручного редактирования конфигов он приятнее (самый известный его «родственник», tsconfig.json, на самом деле JSONC — ещё одно нестандартное надмножество), но строгие RFC 8259-потребители его не принимают. Используйте JSON5/JSONC только там, где потребитель явно их поддерживает, а при выходе в сеть или к произвольному парсеру отдавайте строгий JSON.

«YAML — это JSON с отступами». Любой валидный JSON — валидный YAML, но YAML поверх добавляет якоря и псевдонимы, типы-теги, несколько документов в одном файле, блочные и сложенные скаляры, парсинг, чувствительный к отступам. Классическая ловушка — «норвежская проблема»: в YAML 1.1 неэкранированное NO читается как булево false. Решение «давайте поменяем JSON на YAML ради читаемости» привлекает новые проблемы.

Чек-лист

  1. Вы просто хотите прочитать? Только форматируйте. Не называйте это «валидацией».
  2. Данные пересекают границу доверия (HTTP-запрос, очередь сообщений, конфиг)? Не ограничивайтесь парсингом — добавьте JSON Schema.
  3. Решили ли вы, разрешать ли неизвестные поля? Ставьте в схему additionalProperties: false и определяйте политику отклонения/удаления.
  4. Полезны ли тексты ошибок? Включайте валидатору allErrors: true (как у Ajv), чтобы сразу показывать все нарушения.
  5. Живёт ли схема рядом с кодом? Дрейф между спецификацией и реализацией меньше, когда схема — единый источник истины для обеих сторон.
  6. JSON вообще подходит для этого случая? Если конфиг редактируют люди и вы сами выбираете парсер, JSON5/JSONC или TOML дружелюбнее. Для машинного обмена держитесь строгого JSON.

Связанный инструмент

JSON-форматтер Patrache Studio работает в браузере, и вставленный payload не покидает устройство — удобно, когда нужно посмотреть ответ продакшена, содержащий персональные данные. JSON редко ходит один: если внутри запакованы бинарные данные, в Base64 и URL-кодировании разобрано, почему они распухают и когда лучше взять другой транспорт. Если в payload есть идентификаторы, загляните в Сравнение UUID v1/v4/v7 и дизайн первичного ключа, чтобы понять, как версия, выдаваемая сервером, влияет на индексы, сортировку и кэши ниже по стеку.

Источники