JSON Toolkit

JSON5 і JSONC: JSON із коментарями

Головне: Обидва діалекти існують, бо написана людиною конфігурація потребує коментарів і висячих ком, але їхній синтаксис і охоплення екосистемою різняться, тож неправильний вибір затискає ваш інструментарій.

Початковий~10 хв читанняОпубліковано 2026-10-30Оновлено 2026-10-30
Обкладинка статті про JSON5 і JSONC, JSON із коментарями

Коротко: використовуйте JSONC для коментарів у конфігурації VS Code і TypeScript, використовуйте JSON5, коли потрібен ширший синтаксис літералів ES5.1, як-от одинарні лапки чи ключі без лапок, і тримайте публічні API на стандартному JSON.

Стандартний JSON не дозволяє коментарів, тож кожна команда тихо їх усе одно пише — доки одного дня не ламається збірка. Замість робити це потайки, оберіть належний діалект.

1. Два діалекти мають різні мотивації

  • JSONC (JSON with Comments): стандартний JSON плюс коментарі й висячі коми, майже без інших синтаксичних змін. Це де-факто стандарт екосистеми VS Code — settings.json, tsconfig.json і .eslintrc.json усі його використовують.
  • JSON5: значно амбітніший, прямо вирівняний з об'єктним літералом ES5.1 — одинарні лапки, ключі без лапок, шістнадцяткові числа, Infinity/NaN, продовження рядка зворотною скісною рискою й провідні знаки +.

Одне речення, щоб їх розрізнити: JSONC лише робить написання зручним; JSON5 дозволяє писати конфігураційний файл так, наче це JavaScript.

2. Порівняльна таблиця синтаксису

Можливість Стандартний JSON JSONC JSON5
Коментарі // /* */ ✗ ✓ ✓
Висячі коми ✗ ✓ ✓
Рядки в одинарних лапках ✗ ✗ ✓
Ключі без лапок ✗ ✗ ✓
Шістнадцяткові числа ✗ ✗ ✓
Провідна/завершальна десяткова точка .5 5. ✗ ✗ ✓
Числа, що починаються з + ✗ ✗ ✓
Infinity / NaN ✗ ✗ ✓
Продовження \ у кінці рядка ✗ ✗ ✓
Багаторядкові рядки ✗ ✗ ✓ (реалізовано через продовження)
Довільне значення верхнього рівня ✓ ✓ ✓

Повна специфікація JSON5 трохи більша за сторінку; пакет json5 давно підтримує спільнота, а браузери й Node поводяться ідентично.

3. Розбір: два API, два види здатності позиціонування

3.1 JSON5: простий розбір усього документа

import JSON5 from 'json5';

const config = JSON5.parse(`
{
  // service port
  port: 8080,
  host: '0.0.0.0',
  features: ['json5', 'jsonc',],
  retries: 0x10,
}
`);

console.log(config.port, config.retries); // 8080 16

JSON5.parse повертає звичайний об'єкт без інформації про зміщення — бо він не націлений на сценарії редагування, лише на «читання конфігурації».

3.2 JSONC: синтаксичне дерево зі зміщеннями

Цінність jsonc-parser не в розборі, а в редагуванні. Він пропонує три шари API:

API Призначення Типове використання
parse Розібрати в дерево, що несе offset/length Підсвічування синтаксису, згортання, навігація
visit Обхід у глибину; зворотний виклик отримує шлях вузла Огляд, схожий на JSONPath
modify + applyEdits Створити мінімальну текстову редакцію, потім застосувати її до оригінального тексту Змінити значення, зберігши коментарі
import { parseTree, findNodeAtLocation, modify, applyEdits } from 'jsonc-parser';

const text = `{
  // port configuration
  "server": {
    "port": 8080 // 80 in production
  }
}`;

const tree = parseTree(text);
const portNode = findNodeAtLocation(tree!, ['server', 'port']);
console.log(portNode?.offset, portNode?.length);

const edits = modify(text, ['server', 'port'], 3000, {
  formattingOptions: { insertSpaces: true, tabSize: 2 },
});
console.log(applyEdits(text, edits));

Вивід зберігає оригінальні коментарі й замінює лише три символи 8080:

{
  // port configuration
  "server": {
    "port": 3000 // 80 in production
  }
}

Саме цього «редагування конфігурації регулярними виразами» не може зробити: регулярний вираз не знає, який 8080 змінювати, і не знає, що 8080 усередині рядка треба залишити.

3.3 Повідомлення про помилки мають нести зміщення

Коди помилок jsonc-parser — це значення переліку; поширені:

Код помилки Значення
InvalidSymbol З'явився символ, якого не має бути
PropertyNameExpected Очікувалася назва ключа
ColonExpected Очікувалася двокрапка
CommaExpected Очікувалася кома
CloseBraceExpected Об'єкт не закрито
UnexpectedEndOfComment Коментар не закрито
InvalidEscapeCharacter Невалідний escape

Щойно offset перетворено на рядок і стовпець, редактор може намалювати хвилясту лінію точно на проблемній позиції.

4. Коли що використовувати

Сценарій Рекомендація Причина
Тіла запитів/відповідей публічного API Стандартний JSON Діалекти не підтримуються загальними клієнтами
package.json, composer.json Стандартний JSON Менеджери пакетів розуміють лише стандартний JSON
tsconfig.json, .vscode/settings.json JSONC Типово для екосистеми, рідна підтримка інструментарієм
Конфігурація застосунку (потрібні коментарі) JSONC або YAML Коментарі мають зберігатися, а інструменти все ще можуть читати й писати його
Потрібні Infinity, шістнадцяткові, складні літерали JSON5 Найсильніша виразність
Обмін даних, логи, кеші Формат із розділенням рядків або стандартний JSON Потрібні потоковість і універсальність

Практичне правило: якщо файл читатиме «нелюдський інструмент», надавайте перевагу стандартному JSON; розглядайте діалект лише коли впевнені, що його читає виключно ваш власний код.

5. Три види ризику, які вносять діалекти

5.1 Перетікання потреби

Коли інший інструмент читає конфігураційний файл JSON5 і викликає JSON.parse, він просто падає. Типовий симптом — «локально працює, а в Docker — ні», бо контейнер виконує інший шлях читання.

Запобіжний засіб: додайте в репозиторій перевірку CI, яка явно перелічує, яким файлам дозволено використовувати діалект; усе інше перевіряється як стандартний JSON.

5.2 Несподіванки довкола виведення типів

JSON5 дозволяє ключі без лапок і одинарні лапки, але він не дозволяє вводити літеральні конструкції, як-от дати чи регулярні вирази — це все ще формат даних, а не обчислення JavaScript. Люди припускали, що JSON5 може виражати new Date(), і бачили лише збій розбору. JSON5 — не eval.

5.3 Коментарі мовчки відкидаються

Ані JSON5.stringify, ані JSON.stringify не зберігають коментарів. Тож «прочитати, підправити значення, записати назад» втрачає кожен коментар. Коли коментарі мають вижити, єдиний варіант — API редагування jsonc-parser або бібліотека YAML із підтримкою збереження коментарів.

6. Практичні рекомендації

  • [ ] Тримайте таблицю «тип файлу → формат» у документації команди, щоб ніхто не імпровізував;
  • [ ] Обгорніть читання конфігурації в один loadConfig(), який вирішує всередині, який парсер використовувати, щоб бізнес-код ніколи не викликав JSON.parse безпосередньо;
  • [ ] Програмно змінюючи конфігурацію з коментарями, використовуйте modify + applyEdits; ніколи не переписуйте весь файл;
  • [ ] Перевіряйте файли діалектів у CI теж (jsonc-parser для JSONC, JSON5.parse для JSON5), інакше синтаксичні помилки виявляться лише під час виконання;
  • [ ] Використовуйте стандартний JSON для публічних інтерфейсів, ключів кешу й навантажень черг повідомлень.

Часті питання

Як обирати між JSON5 і JSONC?

Обирайте за середовищем виконання, а не за кількістю синтаксису: VS Code, TypeScript і ESLint за замовчуванням читають JSONC, тож JSONC дає найкращу сумісність; звертайтеся до JSON5 лише коли потрібен ширший синтаксис літералів ES5.1, як-от одинарні лапки, ключі без лапок чи Infinity. Жоден діалект не підходить для відповідей публічного API.

Чому стандартний парсер JSON не може напряму прочитати JSONC?

JSONC дозволяє коментарі й висячі коми, тоді як JSON.parse приймає лише JSON RFC 8259 і кидає виняток на будь-чому з цього. Читання JSONC потребує парсера на кшталт jsonc-parser або видалення коментарів, перш ніж передати текст стандартному парсеру.

Чи мають коментарі JSONC виживати під час редагування файлу?

Так, але лише через API редагування. modify і applyEdits у jsonc-parser змінюють цільовий вузол, зберігаючи решту коментарів; розбір у об'єкт і серіалізація назад відкидає кожен коментар.