Kiedy ścisły JSON każe debugować do późnej nocy z powodu jednego brakującego przecinka, najwyższy czas poznać jego bardziej wyrozumiałych krewnych — JSON5 oraz JSON with Comments (JSONC).
Czym jest JSON5?
JSON5 to rozszerzenie JSON, które przejmuje funkcje z ECMAScript 5.1, w tym klucze bez cudzysłowów, przecinki kończące, łańcuchy w pojedynczych cudzysłowach, komentarze oraz liczby szesnastkowe. Format jest rozwijany przez społeczność i został zaprojektowany tak, aby ręcznie pisane pliki konfiguracyjne były czytelniejsze i bardziej wyrozumiałe niż w przypadku ścisłego JSON.
Czym jest JSONC?
JSONC (JSON with Comments) to minimalne rozszerzenie standardowego JSON, które dodaje obsługę komentarzy jednoliniowych (//) i wieloliniowych (/* */). To, czy dopuszcza także przecinki kończące, zależy od parsera. Pełni funkcję natywnego formatu dla ustawień VS Code, plików tsconfig.json oraz innych plików konfiguracyjnych TypeScript.
Po co nam „wyrozumiały” JSON
- Bariery czytelności: Standardowy JSON zabrania komentarzy, przecinków kończących, pojedynczych cudzysłowów itd., co utrudnia zrozumienie konfiguracji i przykładów.
- Trudność współpracy w zespole: Ogromne pliki JSON bez żadnych adnotacji są trudne w utrzymaniu; drobna edycja może uszkodzić cały plik.
- Realia DevOps: Kubernetes oraz pipeline’y CI/CD często łączą lub generują JSON w locie; wyrozumiała składnia mocno ogranicza ręczne poprawki.
JSON5 a JSONC — szybkie porównanie
| Cecha | JSON (RFC 8259) | JSONC | JSON5 |
|---|---|---|---|
Komentarze // i /* */ | Nie | Tak | Tak |
| Przecinki kończące | Nie | Zależy od parsera (zob. niżej) | Tak, jeden na obiekt lub tablicę |
| Klucze bez cudzysłowów | Nie | Nie | Tak |
| Łańcuchy w pojedynczych cudzysłowach | Nie | Nie | Tak |
| Łańcuchy w kilku wierszach (escapowany znak nowego wiersza) | Nie | Nie | Tak |
Liczby szesnastkowe (0x1F) | Nie | Nie | Tak |
.5, 5., wiodący + | Nie | Nie | Tak |
Infinity, NaN | Nie | Nie | Tak |
Liczby w systemie dwójkowym (0b101) | Nie | Nie | Nie |
| Specyfikacja | RFC 8259 / ECMA-404 | Brak formalnego standardu; w praktyce definiują go VS Code i jsonc-parser | Specyfikacja JSON5 1.0.0 |
Odczyt wbudowanym JSON.parse / json.loads | Tak | Nie | Nie |
Źródła: specyfikacja JSON5, dokumentacja JSON w VS Code i RFC 8259. Wiersz o liczbach dwójkowych sprawdziliśmy sami: JSON5.parse('{a: 0b101}') w json5 2.2.3 kończy się błędem JSON5: invalid character 'b' at 1:6.
W jednym zdaniu: JSON5 sprawia wrażenie literału obiektu JavaScript, podczas gdy JSONC to zwykły JSON, który toleruje komentarze — klucze i łańcuchy nadal muszą być w podwójnych cudzysłowach.
Przecinki kończące w JSONC zależą od parsera
JSONC nie ma oficjalnej specyfikacji. Microsoft wprowadził go nieformalnie na potrzeby plików konfiguracyjnych VS Code i równolegle wydał jsonc-parser. Społecznościowy szkic na jsonc.org próbuje ten format sformalizować, ale o przecinkach kończących mówi tylko tyle, że parsery „MAY support trailing commas” (mogą je obsługiwać). Narzędzia w praktyce zachowują się różnie:
| Parser lub narzędzie | Przecinek kończący | Źródło |
|---|---|---|
| VS Code, tryb JSON with Comments | Akceptowany, z ostrzeżeniem | Dokumentacja VS Code |
jsonc-parser 3.3.1, opcje domyślne | Zgłaszany jako błędy PropertyNameExpected / ValueExpected, ale wartość i tak zostaje zwrócona | Test niżej |
jsonc-parser 3.3.1, allowTrailingComma: true | Akceptowany | Test niżej |
TypeScript 5.9.3 (tsc -p) | Akceptowany | Test na przykładowym tsconfig.json niżej |
Deno (deno.jsonc) | Akceptowany | Dokumentacja konfiguracji Deno |
Jeśli plik ma przyjąć każde narzędzie, które czyta JSONC, lepiej zrezygnować z przecinków kończących.
Przykłady składni
Klasyczny JSON (ścisły)
{
"name": "Go Tools",
"features": ["Base64", "JSON Formatter"]
}
Przykład JSON5
Plik JSON5, config.json5:
// konfiguracja deweloperska
{
name: 'go-tools',
port: 0x1F90, // 8080 szesnastkowo
tags: ['json', 'json5',],
}
Przykład JSONC
Plik JSONC, tsconfig.json:
{
// ścisła kontrola typów
"compilerOptions": {
"strict": true,
"target": "ES2022", /* nowoczesny kod wynikowy */
},
"include": ["src"],
}
Jak rozpoznać format pliku
Samo rozszerzenie niczego nie przesądza: plik .json może być parsowany jako JSONC albo JSON5. Najpierw warto sprawdzić poniższą tabelę, a potem dokumentację narzędzia, które ten plik czyta.
| Plik | Format | Uwagi | Źródło |
|---|---|---|---|
tsconfig.json, jsconfig.json, tsconfig.*.json | JSONC | VS Code otwiera je w trybie JSONC; tsc 5.9.3 akceptuje komentarze i przecinki kończące | Kod źródłowy VS Code (typescript-basics), nasz test |
.vscode/settings.json, tasks.json, launch.json | JSONC | Wymienione jako pliki JSON with Comments | Dokumentacja VS Code |
devcontainer.json | JSONC | Specyfikacja nazywa go „a JSON with Comments file” (plik JSON with Comments) | Specyfikacja Dev Container |
deno.jsonc | JSONC | „with deno.jsonc you can add comments and trailing commas” (w deno.jsonc można dodawać komentarze i przecinki kończące) | Dokumentacja Deno |
babel.config.json, .babelrc.json | JSON5, mimo rozszerzenia .json | Babel parsuje je jako JSON5; VS Code podświetla je jako JSONC | Dokumentacja Babel, kod źródłowy VS Code |
renovate.json5, .renovaterc.json5 | JSON5 | renovate.json przyjmuje też JSONC | Dokumentacja Renovate |
Dowolny plik *.json5 | JSON5 | — | — |
package.json | Ścisły JSON | npm: „It must be actual JSON, not just a JavaScript object literal.” (musi to być prawdziwy JSON, a nie tylko literał obiektu JavaScript) | Dokumentacja npm, nasz test |
Babel nie szuka pliku babel.config.json5: rozszerzenia .json5 nie ma wśród tych, które opisuje jego dokumentacja. Jako JSON5 czyta właśnie warianty z rozszerzeniem .json.
Komunikaty błędów w dosłownym brzmieniu
Te same dwa małe pliki przekazaliśmy każdemu środowisku uruchomieniowemu. comment.json zawiera komentarz:
{
// port serwera deweloperskiego
"port": 8080
}
trailing.json ma przecinek kończący:
{
"port": 8080,
}
Node.js 26.10.0, JSON.parse (Chrome Headless Shell 156 zwraca dokładnie ten sam tekst, bo oba korzystają z V8):
comment.json -> SyntaxError: Expected property name or '}' in JSON at position 4 (line 2 column 3)
trailing.json -> SyntaxError: Expected double-quoted property name in JSON at position 18 (line 3 column 1)
Python 3.13.5, json.loads:
comment.json -> json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 2 column 3 (char 4)
trailing.json -> json.decoder.JSONDecodeError: Illegal trailing comma before end of object: line 2 column 15 (char 16)
jq 1.7:
comment.json -> jq: parse error: Invalid numeric literal at line 2, column 5
trailing.json -> jq: parse error: Expected another key-value pair at line 3, column 1
npm 11.19.1, z wierszem // comment w package.json:
npm error code EJSONPARSE
npm error JSON.parse Invalid package.json: JSONParseError: Expected property name or '}' in JSON at position 4 (line 2 column 3) while parsing near "{\n // comment\n \"name\":..."
npm error JSON.parse Failed to parse JSON data.
npm error JSON.parse Note: package.json must be actual JSON, not just JavaScript.
Jak je czytać:
position 4 (line 2 column 3)wskazuje pierwszy/komentarza. Jeśli błąd wypada na początku wiersza, który zaczyna się od//lub/*, plik jest niemal na pewno w formacie JSONC albo JSON5.- Przy przecinkach kończących V8 i Python wskazują różne miejsca. V8 zgłasza zamykający
}(wiersz 3), a Python 3.13.5 sam przecinek (wiersz 2, kolumna 15). Inne wersje mogą formułować te komunikaty inaczej; powyższy tekst to dokładnie to, co wypisały wymienione wersje. - Jeśli komunikat cytuje znak, którego nie widać, jak w Node 26:
Unexpected token '', "{}" is not valid JSON(w obu cudzysłowach siedzi w rzeczywistości niewidoczny znak U+FEFF), plik najpewniej zaczyna się od niewidocznego znacznika kolejności bajtów (BOM), a nie od komentarza. Więcej w artykule BOM UTF-8 a błędy JSON.parse.
Konwersja JSONC lub JSON5 do standardowego JSON
Konwersja usuwa wszystkie komentarze, bo JSON nie ma gdzie ich przechować. Plik z komentarzami warto traktować jako źródło, a wynikowy .json jako wygenerowany artefakt.
Node.js: JSON5 do JSON za pomocą json5
// json5-to-json.mjs (instalacja: npm install json5)
import { readFileSync, writeFileSync } from 'node:fs';
import JSON5 from 'json5';
const data = JSON5.parse(readFileSync('config.json5', 'utf8'));
writeFileSync('config.json', JSON.stringify(data, null, 2) + '\n');
Po uruchomieniu z json5 2.2.3 na przykładzie JSON5 z sekcji powyżej plik config.json wygląda tak:
{
"name": "go-tools",
"port": 8080,
"tags": [
"json",
"json5"
]
}
Node.js: JSONC do JSON za pomocą jsonc-parser
jsonc-parser to parser Microsoftu dla JSON z komentarzami. Jego parse() nie rzuca wyjątku przy błędach składni. Problemy zbiera w tablicy errors i mimo to zwraca wartość odczytaną najlepiej, jak się da, więc tę tablicę trzeba sprawdzić samodzielnie:
// jsonc-to-json.mjs (instalacja: npm install jsonc-parser)
import { readFileSync } from 'node:fs';
import { parse, printParseErrorCode } from 'jsonc-parser';
const errors = [];
const data = parse(readFileSync('tsconfig.json', 'utf8'), errors, {
allowTrailingComma: true,
});
if (errors.length > 0) {
for (const e of errors) console.error(printParseErrorCode(e.error), 'at offset', e.offset);
process.exit(1);
}
console.log(JSON.stringify(data, null, 2));
Z jsonc-parser 3.3.1 i przykładowym tsconfig.json z sekcji powyżej skrypt wypisuje czysty JSON i kończy działanie z kodem 0. Bez allowTrailingComma: true ten sam plik zgłasza PropertyNameExpected i ValueExpected na znaku } tuż po każdym przecinku kończącym.
Python: pakiet json5
Moduł json z biblioteki standardowej nie ma opcji obsługi komentarzy. Pakiet json5 czyta JSON5, a więc także JSONC, bo komentarze i przecinki kończące są w JSON5 dozwolone:
# instalacja: pip install json5
import json
import json5
with open("tsconfig.json", encoding="utf-8") as f:
data = json5.load(f)
print(json.dumps(data, indent=2, ensure_ascii=False))
Przetestowano w Pythonie 3.13.5 z json5 0.17.3: skrypt wypisuje ten sam JSON co wersja dla Node.js.
Wiersz poleceń: CLI json5
Pakiet npm json5 zawiera CLI, które konwertuje do JSON i waliduje składnię. Czyta też pliki JSONC:
npx json5 config.json5 # zwarty JSON na stdout
npx json5 -s 2 -o config.json config.json5 # z wcięciami, zapis do pliku
npx json5 -v tsconfig.json # tylko walidacja; kod wyjścia 1 przy błędzie składni
Ponieważ -v przy błędzie składni kończy działanie z kodem 1, to samo polecenie sprawdzi się jako krok CI, który kontroluje ręcznie edytowane pliki JSON5 lub JSONC.
jq tu nie pomoże: jak widać wyżej, jq 1.7 zatrzymuje się na pierwszym komentarzu.
Uwaga na Infinity i NaN
JSON5 dopuszcza Infinity i NaN, ale w JSON nie da się ich zapisać, a JavaScript i Python radzą sobie z tym inaczej:
JSON.stringify(JSON5.parse('{a: Infinity, b: NaN}'));
// '{"a":null,"b":null}' (po cichu zamienia się w null)
json.dumps(json5.loads('{a: Infinity, b: NaN}'))
# '{"a": Infinity, "b": NaN}' (wynik, który nie jest poprawnym JSON)
json.dumps(json5.loads('{a: Infinity}'), allow_nan=False)
# ValueError: Out of range float values are not JSON compliant: inf
Jeśli plik JSON5 może zawierać te wartości, w Pythonie warto przekazać allow_nan=False albo sprawdzić je przed konwersją. Łańcuchy JSON5 w pojedynczych cudzysłowach, które zawierają " lub ukośniki odwrotne, JSON.stringify escapuje na nowo. Zasady opisuje przewodnik po escapowaniu ciągów JSON.
Walidacja skonwertowanego JSON
Po konwersji wynik można wkleić do narzędzia Formatowanie JSON, aby go zwalidować i sformatować (pretty-print). Narzędzie działa w przeglądarce.
Uwaga: formater akceptuje wyłącznie ścisły JSON. Parsuje dane przeglądarkowym JSON.parse, więc plik JSONC lub JSON5 wklejony bez zmian kończy się błędem składni z numerem wiersza i kolumny, w których parsowanie się zatrzymało. Najpierw konwersja, potem walidacja.
Kiedy nie używać JSONC ani JSON5
- Payload API i wymiana danych. RFC 8259 mówi, że generator JSON „MUST strictly conform to the JSON grammar” (musi ściśle przestrzegać gramatyki JSON). Po stronie odbiorcy zwykle działa
JSON.parselubjson.loads, a oba odrzucają komentarze. package.json. npm wymaga ścisłego JSON. Wystarczy jeden komentarz, by polecenia npm, takie jaknpm run, kończyły się błędemEJSONPARSE.- Pliki czytane przez narzędzia, nad którymi nie ma się kontroli.
require('./tsconfig.json')w Node.js używa ścisłego parsera JSON i zawodzi na tsconfig z komentarzami (SyntaxError: …tsconfig.json: Expected property name or '}' in JSON at position 4). Każdy skrypt, który czyta plik JSONC, potrzebuje parsera JSONC. - Plik
.json, który po cichu stał się JSONC. Dokumentacja Renovate zaleca plik.jsonczamiast pliku.jsonz komentarzami, bo ten drugi „can cause issues with editors and syntax highlighting” (może sprawiać problemy edytorom i podświetlaniu składni). Jawne rozszerzenie mówi każdemu narzędziu, czego się spodziewać.
Jeśli format konfiguracji wybiera się od zera, a komentarze mają działać wszędzie, TOML i YAML obsługują je natywnie. Więcej w porównaniu TOML vs JSON vs YAML.
Pliki konfiguracyjne w realnym świecie
JSON5 oraz JSONC są już szeroko używane w plikach konfiguracyjnych spotykanych na co dzień. Oto konkretne przykłady:
TypeScript (tsconfig.json — JSONC)
{
// Strict mode catches more bugs at compile time
"compilerOptions": {
"strict": true,
"target": "ES2022",
"module": "ESNext",
"outDir": "./dist",
// Path aliases for cleaner imports
"paths": {
"@/*": ["./src/*"],
"@tests/*": ["./tests/*"],
},
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"],
}
Plik tsconfig.json w TypeScript natywnie korzysta z JSONC — komentarze i przecinki kończące działają od razu. To jeden z najczęściej spotykanych plików JSONC w każdym projekcie JavaScript/TypeScript.
Babel (babel.config.json — JSON5)
Babel parsuje babel.config.json i .babelrc.json jako JSON5, więc klucze bez cudzysłowów i pojedyncze cudzysłowy działają w nich bez problemu.
{
// Single quotes and unquoted keys make it more readable
presets: [
['@babel/preset-env', {
targets: '> 0.25%, not dead',
modules: false, // Let bundler handle modules
}],
],
plugins: [
'@babel/plugin-transform-runtime',
],
}
Ustawienia obszaru roboczego VS Code (.vscode/settings.json — JSONC)
{
// Editor preferences
"editor.fontSize": 14,
"editor.tabSize": 2,
"editor.formatOnSave": true,
// Language-specific overrides
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
},
}
Praktyczne wskazówki i pułapki
1. Konwersja przed wdrożeniem na produkcję
Przeglądarki nadal wymagają ścisłego JSON dla JSON.parse. Konwersję warto więc wykonać na etapie builda, tak jak pokazano wyżej w sekcji o konwersji JSONC i JSON5 do standardowego JSON.
2. Bezpieczeństwo i niezawodność
Pobłażliwość może maskować nadmiarowe pola. Konfigurację należy zawsze walidować z użyciem JSON Schema lub zod, zanim zostanie ona zużyta:
// Validate config shape with zod after parsing
import { readFileSync } from 'node:fs';
import { z } from 'zod';
import JSON5 from 'json5';
const ConfigSchema = z.object({
port: z.number().min(1).max(65535),
host: z.string(),
debug: z.boolean().default(false),
});
const raw = JSON5.parse(readFileSync('config.json5', 'utf8'));
const config = ConfigSchema.parse(raw); // Throws if invalid
3. Konwencje zespołowe
- Warto zaznaczyć w README, że pliki
*.json5służą wyłącznie do konfiguracji; pakiety wydawane do publikacji powinny zawierać ścisłe.json. - Należy ustandaryzować reguły Prettier, aby ograniczyć szum w diffach.
- Plik
.editorconfigpomaga wymusić spójne wcięcia w plikach JSON, JSON5 oraz JSONC.
4. Wydajność
Parsowanie JSON5/JSONC dokłada zależności i lekki narzut — to do przyjęcia w narzędziach CLI i deweloperskich, ale na produkcji warto to zmierzyć. Dla plików konfiguracyjnych ładowanych raz przy starcie różnica jest pomijalna. Dla gorących ścieżek przetwarzających tysiące obiektów na sekundę lepiej pozostać przy ścisłym JSON.
Najczęściej zadawane pytania
Jaka jest różnica między JSON5 a JSONC?
JSONC to standardowy JSON z komentarzami // i /* */, a to, czy dopuszcza przecinki kończące, zależy od parsera. JSON5 idzie dalej i przejmuje składnię ES5.1: klucze bez cudzysłowów, pojedyncze cudzysłowy, liczby szesnastkowe, Infinity/NaN oraz przecinki kończące. Poprawny plik JSONC jest zwykle poprawnym plikiem JSON5, ale nie na odwrót. JSONC wybierze się do plików konfiguracyjnych, gdzie wystarczą komentarze; JSON5 sprawdzi się tam, gdzie potrzeba bogatszej składni.
Czy można używać JSON5 lub JSONC w produkcyjnych API?
Nie — JSON5 i JSONC są przeznaczone do plików konfiguracyjnych, a nie do wymiany danych. API powinny korzystać ze standardowego JSON (RFC 8259), aby zapewnić maksymalną interoperacyjność. JSON5/JSONC warto zarezerwować dla lokalnych plików konfiguracyjnych takich jak tsconfig.json, .vscode/settings.json lub konfiguracji środowiska, gdzie czytelność dla człowieka liczy się bardziej niż przetwarzanie maszynowe.
Czy VS Code natywnie obsługuje JSON5?
Nie w standardowej instalacji. Wbudowane rozszerzenie JSON w VS Code rejestruje m.in. języki json, jsonc i jsonl, ale nie json5, więc pliki .json5 potrzebują rozszerzenia z marketplace, żeby mieć podświetlanie składni. JSONC jest wbudowany: ustawienia, tasks.json, launch.json i tsconfig.json otwierają się w trybie JSON with Comments.
Jak skonwertować JSON5 do standardowego JSON?
Wystarczy użyć pakietu npm json5: JSON5.parse() wczytuje JSON5, a następnie JSON.stringify() zwraca standardowy JSON. W wersji CLI można uruchomić npx json5 < input.json5 > output.json. Komentarze przy tym przepadają, a Infinity i NaN w JSON.stringify zamieniają się w null. Narzędzia online, takie jak nasz JSON Formatter, potrafią również zwalidować skonwertowany wynik, aby potwierdzić jego poprawność.
Czy JSONC to to samo co JSON with Comments?
Tak. „JSON with Comments” to nazwa trybu językowego w VS Code, a jsonc to jego identyfikator języka i rozszerzenie pliku. Format nie ma formalnego standardu; na jsonc.org dostępny jest społecznościowy szkic oparty na tym, co jsonc-parser akceptuje w ustawieniach domyślnych. Do standardowego JSON dochodzą komentarze // i /* */, a obsługa przecinków kończących zależy od parsera.
Aby poznać więcej narzędzi deweloperskich działających w przeglądarce — w tym kodowanie Base64, generowanie sum kontrolnych i konwersję timestampów — warto zajrzeć do naszego przewodnika po niezbędnych narzędziach dla programistów.
Podsumowanie
Zachowanie równowagi między czytelnością a rygorem to klasyczne wyzwanie deweloperskie. JSON5 oraz JSONC poprawiają komfort pisania, a w połączeniu z odpowiednim pipeline’em buildowania i walidacji można bezpiecznie cieszyć się zaletami „pobłażliwego JSON”.