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 змінюють цільовий вузол, зберігаючи решту коментарів; розбір у об'єкт і серіалізація назад відкидає кожен коментар.