Перевірка JSON у CI: конвеєр із jsonlint, ajv і jq
Головне: Перевірка синтаксису доводить, що файл розбирається; перевірка схеми доводить, що поля відповідають контракту. У CI важливі однозначні коди виходу, локалізація помилок і шляхи виправлення.

Коротко: у CI доручіть jq empty синтаксис, ajv-cli структуру (check-jsonschema для команд із кількома форматами), а Prettier — стиль; зчепіть їх за шарами й зупиняйтеся на першому збої.
Проходження парсера у вашому локальному редакторі не означає, що файл належить до CI; проходження CI теж не означає, що поля відповідають контракту.
1. Відділіть перевірку синтаксису від перевірки схеми
Фразу «перевірка JSON» використовують занадто широко, і саме тому команди часто обирають один інструмент для двох різних проблем:
| Шар | На яке питання відповідає | Типові інструменти | Що означає збій |
|---|---|---|---|
| Синтаксис | Чи цей файл валідний JSON? | jq empty, jsonlint, python -m json.tool |
Дужки, коми, лапки або кодування зламані |
| Схема | Чи відповідають поля, типи й обов'язкові властивості контракту? | ajv-cli, check-jsonschema |
Файл розбирається, але бізнес-структура неправильна |
| Стиль | Чи узгоджені відступи й порядок ключів? | Prettier, jq -S |
Форматування неузгоджене, diff шумний |
Ці шари не можуть заміняти один одного. Файл, що проходить синтаксис, усе ще може провалити схему: {"price": "12.5"} — валідний JSON, але якщо схема вимагає price числом, він неприйнятний. І навпаки, якою б повною не була схема, файл без закривної дужки ніколи не скомпілюється проти неї.
Тож конвеєр має запускати спершу синтаксис, потім структуру, а врешті стиль. Зупиняйтеся, щойно якийсь шар зазнає збою, інакше помилка розбору маскується під помилку поля.
2. Шар синтаксису: jq empty — варіант із мінімумом залежностей
Якщо jq вже є в образі CI, перевірка синтаксису майже безкоштовна:
jq empty data.json
jq empty не дає жодного виводу; він лише розбирає файл. Він завершується з 0 у разі успіху й з ненульовим кодом плюс інформацією про рядок/стовпець у разі збою. Це робить його легшим за підтягування повного дерева залежностей Node і добрим першим бар'єром.
Під час пакетної перевірки файлів JSON у репозиторії виключіть node_modules, результати збірки й кеші третіх сторін:
find . -name '*.json' \
-not -path './node_modules/*' \
-not -path './dist/*' \
-print0 | xargs -0 -n1 jq empty
-print0 і xargs -0 обробляють пробіли в назвах файлів, а -n1 гарантує один файл на виклик, тож повідомлення про помилку вказує на винуватця. Не передавайте сотні файлів одному процесу; повідомлення про помилки перезаписуватимуть одне одного.
jsonlint — інший поширений вибір, який зазвичай викликають як npx jsonlint -q data.json, де -q виводить лише помилки. Він повідомляє номери рядків і стовпців, але його підтримка менш активна за jq, а різні версії по-різному толерують BOM і коментарі. Сприймайте його як читабельнішу перевірку синтаксису, а не як валідатор схем.
3. Шар схеми: основи ajv-cli validate
Ajv — найпоширеніший валідатор схем в екосистемі JavaScript, а ajv-cli відкриває його як інструмент командного рядка:
ajv validate -s schema.json -d data.json
-s указує на файл схеми, а -d — на дані, що перевіряються. У разі збою процес завершується з ненульовим кодом, тож CI падає одразу. Справжні проєкти зазвичай додають два прапорці:
npx ajv-cli validate \
-s schemas/order.json \
-d 'fixtures/orders/*.json' \
--strict=false \
--errors=text
--strict=false допомагає зі старішими схемами: строгий режим трактує невідомі ключові слова й підозрілі оголошення required як помилки, на чому зазнають збою багато застарілих схем. --errors=text дає зрозумілий для людини вивід, тоді як типова структура JSON краща для машин. Обирайте залежно від того, хто споживає результат.
Версія чернетки схеми теж має збігатися. Основні версії Ajv за замовчуванням підтримують різні чернетки, і ajv-cli може обрати одну через --spec=draft7, --spec=draft2019 або --spec=draft2020. Якщо схема оголошує $schema: "https://json-schema.org/draft/2020-12/schema", а CLI компілює її як draft-07, деякі ключові слова мовчки ігноруються. Про написання схем і відмінності версій дивіться Початок роботи з JSON Schema.
Перевірка форматів — ще одна часта вимога. Ключове слово format за замовчуванням лише анотаційне; щоб email, date-time і uri справді щось забезпечували, завантажте ajv-formats:
npx ajv-cli validate -c ajv-formats -s schema.json -d data.json
Цей крок легко пропустити, і результат такий, що поле email приймає not-an-email. Перед релізом подайте конвеєру один навмисно невалідний запис; це доводить, що правило активне, набагато краще, ніж десятиразове читання документації.
check-jsonschema: кілька форматів і дружніші помилки
Якщо команда обробляє, окрім JSON, ще YAML, TOML або конфігурацію GitHub Actions, check-jsonschema зручніший:
check-jsonschema --schemafile schemas/config.json config/app.json
check-jsonschema --schemafile schemas/pipeline.json .github/workflows/ci.yml
Його практичні переваги:
- Рідна підтримка YAML, що підходить для файлів «форми JSON, але написаних YAML», як-от маніфести Kubernetes і конфігурація CI.
- Помилки містять шлях файлу, номер рядка й шлях поля, що тримає витрати на локалізацію низькими.
- Надається хук pre-commit, тож інтеграція дешева.
- Він може підтягувати поширені схеми з SchemaStore, але також приймає локальні файли, уникаючи мережевої залежності.
У кросс-мовних командах Python часто вже присутній, тож встановлення й оновлення check-jsonschema легше, ніж підтримка інструментарію Node. Доручіть jq синтаксис, а цьому інструменту — структуру; комбінація має дуже низькі витрати на підтримку.
4. Коди виходу — єдиний надійний сигнал для CI
Яким би вишліфованим не був вивід, CI читає лише коди виходу. Три пастки поширені:
| Симптом | Причина | Виправлення |
|---|---|---|
| Помилки є, але CI зелений | Команда закінчується на ` | |
| Помилок немає, але CI червоний | diff трактує «знайдено відмінності» як збій |
Розрізняйте «відмінність» і «помилку виконання» |
| Вивід помилок обрізано | Паралельний вивід кількох файлів перезаписує себе | Запускайте послідовно або виводьте посторінково |
Пам'ятайте семантику кодів виходу jq: нормальний розбір повертає 0; з -e результат виразу false або null повертає 1; помилки використання й відсутні файли повертають 2; помилки компіляції скрипта повертають 3. Інструменти схем зазвичай використовують 0 для успіху, 1 для збою перевірки й 2 для помилок інструмента чи аргументів. Пишучи обгортку, не згортайте кожен ненульовий код у «дані неправильні», інакше збій середовища буде помилково діагностовано як проблему вмісту.
5. pre-commit: ловіть проблеми до коміту
Цінність pre-commit — не «ще одна перевірка», а зворотний зв'язок розробникам за хвилину, а не після десятихвилинного запуску CI. Мінімальна конфігурація виглядає так:
repos:
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: 0.33.0
hooks:
- id: check-jsonschema
files: ^configs/.*\.json$
args:
- --schemafile
- schemas/config.json
- repo: local
hooks:
- id: jq-syntax
name: jq syntax check
entry: jq empty
language: system
files: ^configs/.*\.json$
Важливі дві деталі:
filesмає відповідати лише каталогам, що потребують перевірки. Не запускайте хуки протиnode_modulesчи згенерованих файлів.- Локальний хук припускає, що
jqвстановлено на машині розробника. Документуйте цю вимогу або перемкніться на контейнеризований хук.
Якщо проєкт уже використовує Husky і lint-staged, поєднайте їх безпосередньо:
{
"lint-staged": {
"configs/*.json": ["jq empty", "prettier --check"]
}
}
Пам'ятайте, що lint-staged передає список доданих файлів, тож команда має приймати кілька шляхів. И jq empty, і prettier --check це роблять. Не дозволяйте хуку сканувати весь репозиторій; коміти помітно сповільняться.
6. GitHub Actions: конвеєр, який можна копіювати
Наведений нижче робочий процес виконує перевірку синтаксису, перевірку схеми й зберігає звіт у разі збою:
name: json-validate
on:
push:
paths:
- 'schemas/**'
- 'fixtures/**'
pull_request:
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Syntax check
run: |
find fixtures -name '*.json' -print0 | xargs -0 -n1 jq empty
- name: Schema check
run: |
npx --yes ajv-cli validate \
-s schemas/order.json \
-d 'fixtures/orders/*.json' \
--strict=false \
--errors=text
Фільтр paths не дає змінам лише в документації запускати перевірку. npx --yes підходить репозиторіям без закріплених залежностей; виробничі проєкти мають додати ajv-cli у devDependencies і закріпити версію. Без блокування реліз вище за течією, що змінює чернетку за замовчуванням чи строгий режим, може зробити конвеєр червоним за одну ніч.
7. Локалізація помилок: від номерів рядків до шляхів полів
Половина цінності валідатора — виявлення; друга половина — локалізація. Навчіться читати три види виводу:
- Синтаксичні помилки:
jqповідомляє рядок і стовпець, часто з сусіднім фрагментом. Спершу виправте дужки й коми; не підозрюйте одразу кодування. - Помилки схеми:
instancePathAjv вказує на місце даних (наприклад,/items/0/price),schemaPathвказує на правило (наприклад,#/properties/items/items/properties/price/type), аkeywordназиває порушене обмеження. - Помилки форматів: коли перевірка
formatзазнає збою, повідомлення часто каже лише «must match format "email"». Поєднайте його зinstancePath, щоб визначити поле.
Вставте instancePath у запис розслідування; це набагато корисніше за стіну JSON. Для масивів числовий індекс також каже, який запис зазнав збою. Якщо бізнес-ключ інтуїтивніший, додайте рядок відображення, як-от items[0].id = ORD-042.
Поширені помилки схем, як-от надмірне використання additionalProperties: false або написання гілок oneOf, що насправді перетинаються, розглянуто в Поширені пастки генерації JSON Schema. Неправильне правило перевірки небезпечніше за відсутність перевірки, бо створює фальшиве відчуття безпеки.
8. Таблиця вибору інструментів
| Потреба | Рекомендований інструмент | Чому | Застереження |
|---|---|---|---|
| Лише синтаксис | jq empty |
Нуль залежностей, швидкий, чіткі коди виходу | Пробіли в назвах файлів під час пакетних запусків |
| Дружніші помилки рядків/стовпців | jsonlint |
Читабельний вивід | Підтримка обмежена |
| Перевірка JSON Schema | ajv-cli |
Зрела екосистема, структуровані помилки | Узгодьте версії чернеток і завантажте підтримку format |
| Змішаний репозиторій JSON і YAML | check-jsonschema |
Кілька форматів, дружній до pre-commit | Джерело схеми й мережевий доступ |
| Швидкий зворотний зв'язок перед комітом | pre-commit / lint-staged | Обробляє лише додані файли | Хуки мають приймати кілька шляхів |
| Узгоджене форматування | Prettier | Широко впроваджений | Використовуйте --check, коли не хочете переписувати файли |
Остання рекомендація: переконайтеся, що конвеєр справді падає, подавши йому спершу один навмисно невалідний запис. Найбільший ризик — не складна конфігурація, а конфігурація, що ніколи не набуває чинності. Вважати «воно може впасти» критерієм приймання надійніше, ніж вважати таким «воно проходить». Про ширший інструментарій командного рядка продовжіть у Редактори, командний рядок і автоматизація.
Часті питання
Використовувати jq empty чи jsonlint?
Надавайте перевагу jq empty у CI: нуль залежностей, швидкий старт і чітка семантика кодів виходу. Використовуйте jsonlint, коли потрібні дружніші повідомлення про рядок/стовпець або розслідуєте локально. Обидва виконують лише перевірку синтаксису; жоден не перевіряє структуру полів.
Чому ajv повідомляє про невідоме ключове слово?
Спершу підтвердьте, що чернетка $schema схеми збігається з версією, яку використовує CLI. Багато помилок «невідоме ключове слово» — це ключові слова нової чернетки, на які натрапив старіший компілятор. Якщо версії збігаються, перевірте на друкарську помилку. --strict=false може зберегти працездатність застарілих схем, але не покладайтеся на нього довгостроково, щоб ховати справжні проблеми.
Перевірка схеми проходить, але API все одно зазнає збою. Чому?
Схема перевіряє структуру, а не бізнес-семантику. Наприклад, status може бути рядком із законним переліком, тоді як скінченний автомат забороняє прямий перехід від paid до refunded. Такі правила належать коду або обмеженням бази даних, а не JSON Schema.
Як швидко знайти поле, що зазнало збою?
Прочитайте instancePath або номер рядка з виводу інструмента. instancePath Ajv — це JSON Pointer, що вказує безпосередньо на дані; check-jsonschema повідомляє файл і рядок. Запишіть шлях і відобразіть його на бізнес-ключ; локалізація проблеми зазвичай займає хвилини.
Чи має pre-commit виконувати повну перевірку?
Ні. pre-commit має обробляти лише додані файли, щоб коміти залишалися швидкими. Повні сканування належать CI або запланованим задачам. Поділ простий: локальні запуски на користь швидкості, CI на користь повноти.