JSON Toolkit

JSON5 i JSONC: JSON z komentarzami

Podsumowanie: Oba dialekty istnieją, bo konfiguracja pisana przez ludzi wymaga komentarzy i przecinków końcowych, ale ich składnia i zasięg ekosystemu się różnią, więc zły wybór zakleszcza łańcuch narzędzi.

Początkujący~10 min czytaniaOpublikowano 2026-10-30Zaktualizowano 2026-10-30
Okładka artykułu o JSON5 i JSONC, JSON-ie z komentarzami

Najważniejsze: używaj JSONC do komentarzy w konfiguracji VS Code i TypeScript, używaj JSON5, gdy potrzebujesz szerszej składni literałów ES5.1, takiej jak pojedyncze cudzysłowy lub klucze bez cudzysłowów, a publiczne API pozostawiaj na standardowym JSON-ie.

Standardowy JSON nie pozwala na komentarze, więc każdy zespół po cichu i tak je pisze — aż pewnego dnia budowanie się psuje. Zamiast robić to potajemnie, wybierz właściwy dialekt.

1. Dwa dialekty mają różne motywacje

  • JSONC (JSON with Comments): standardowy JSON plus komentarze i przecinki końcowe, bez prawie żadnych innych zmian składni. To de facto standard ekosystemu VS Code — settings.json, tsconfig.json i .eslintrc.json używają go.
  • JSON5: znacznie ambitniejszy, wyrównany bezpośrednio do literału obiektowego ES5.1 — pojedyncze cudzysłowy, klucze bez cudzysłowów, szesnastkowe, Infinity/NaN, kontynuacja wiersza backslashem i wiodące znaki +.

Jedno zdanie, aby je rozróżnić: JSONC tylko czyni pisanie wygodnym; JSON5 pozwala pisać plik konfiguracyjny tak, jakby był JavaScriptem.

2. Tabela porównania składni

Cecha Standardowy JSON JSONC JSON5
Komentarze // /* */ ✗ ✓ ✓
Przecinki końcowe ✗ ✓ ✓
Łańcuchy w pojedynczych cudzysłowach ✗ ✗ ✓
Klucze bez cudzysłowów ✗ ✗ ✓
Liczby szesnastkowe ✗ ✗ ✓
Wiodąca/końcowa kropka dziesiętna .5 5. ✗ ✗ ✓
Liczby zaczynające się od + ✗ ✗ ✓
Infinity / NaN ✗ ✗ ✓
Kontynuacja \ na końcu wiersza ✗ ✗ ✓
Łańcuchy wieloliniowe ✗ ✗ ✓ (zrealizowane przez kontynuację)
Dowolna wartość wierzchołkowa ✓ ✓ ✓

Pełna specyfikacja JSON5 liczy ledwie więcej niż stronę; pakiet json5 jest od dawna utrzymywany przez społeczność, a przeglądarki i Node zachowują się identycznie.

3. Parsowanie: dwa API, dwa rodzaje możliwości lokalizacji

3.1 JSON5: proste parsowanie całego dokumentu

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 zwraca zwykły obiekt bez informacji o przesunięciach — bo nie jest nastawione na scenariusze edycji, tylko na „czytanie konfiguracji”.

3.2 JSONC: drzewo składni z przesunięciami

Wartość jsonc-parser nie leży w parsowaniu, ale w edycji. Oferuje trzy warstwy API:

API Cel Typowe użycie
parse Parsowanie do drzewa niosącego offset/length Podświetlanie składni, zwijanie, nawigacja
visit Przejście w głąb; callback otrzymuje ścieżkę węzła Inspekcja w stylu JSONPath
modify + applyEdits Wytworzenie minimalnej edycji tekstu, a następnie zastosowanie jej do oryginału Zmiana wartości z zachowaniem komentarzy
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));

Wyjście zachowuje oryginalne komentarze i zastępuje tylko trzy znaki 8080:

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

Właśnie tego nie potrafi „edycja konfiguracji wyrażeniami regularnymi”: regex nie wie, który 8080 zmienić, i nie wie, że 8080 wewnątrz łańcucha trzeba zostawić.

3.3 Komunikaty błędów muszą nieść przesunięcia

Kody błędów z jsonc-parser to wartości wyliczeniowe; najczęstsze to:

Kod błędu Znaczenie
InvalidSymbol Pojawił się znak, którego nie powinno być
PropertyNameExpected Oczekiwano nazwy klucza
ColonExpected Oczekiwano dwukropka
CommaExpected Oczekiwano przecinka
CloseBraceExpected Obiekt nie został zamknięty
UnexpectedEndOfComment Komentarz nie został zamknięty
InvalidEscapeCharacter Niepoprawna ucieczka

Gdy offset zostanie przetłumaczone na wiersz i kolumnę, edytor potrafi narysować falę dokładnie w miejscu błędu.

4. Kiedy czego użyć

Scenariusz Zalecenie Powód
Treści żądań i odpowiedzi publicznych API Standardowy JSON Dialekty nie są wspierane przez uniwersalnych klientów
package.json, composer.json Standardowy JSON Menedżery pakietów rozumieją tylko standardowy JSON
tsconfig.json, .vscode/settings.json JSONC Domyślny wybór ekosystemu, natywnie wspierany przez łańcuch narzędzi
Konfiguracja aplikacji (wymaga komentarzy) JSONC lub YAML Komentarze muszą przetrwać, a narzędzia nadal muszą czytać i zapisywać
Potrzeba Infinity, szesnastkowych, złożonych literałów JSON5 Największa siła wyrazu
Wymiana danych, logi, cache Format liniowy lub standardowy JSON Wymagane strumieniowanie i uniwersalność

Zasada praktyczna: jeśli plik będzie czytany przez „narzędzie nieludzkie”, preferuj standardowy JSON; rozważ dialekt tylko wtedy, gdy masz pewność, że czyta go wyłącznie twój własny kod.

5. Trzy rodzaje ryzyka, które wprowadzają dialekty

5.1 Rozlanie się wymagania

Gdy inne narzędzie czyta plik konfiguracyjny JSON5 i wywołuje JSON.parse, natychmiast się wysypuje. Typowy objaw to „działa lokalnie, zawodzi na Dockerze” — bo kontener uruchamia inną ścieżkę odczytu.

Środek zaradczy: dodaj do repozytorium kontrolę CI, która jawnie wymienia pliki mogące używać dialektu; wszystko inne jest walidowane jako standardowy JSON.

5.2 Niespodzianki wokół wnioskowania typów

JSON5 pozwala na klucze bez cudzysłowów i pojedyncze cudzysłowy, ale nie pozwala wnosić literałów takich jak daty czy wyrażenia regularne — to nadal format danych, a nie ewaluacja JavaScript. Ludzie zakładali, że JSON5 potrafi wyrazić new Date(), by zobaczyć, że parsowanie zawodzi. JSON5 to nie eval.

5.3 Komentarze po cichu odrzucane

Ani JSON5.stringify, ani JSON.stringify nie zachowuje komentarzy. Tak więc „wczytaj, popraw wartość, zapisz z powrotem” gubi każdy komentarz. Gdy komentarze muszą przetrwać, jedyne opcje to API edycji jsonc-parser albo biblioteka YAML wspierająca zachowanie komentarzy.

6. Praktyczne zalecenia

  • [ ] Trzymaj w dokumentacji zespołu tabelę „typ pliku → format”, aby nikt nie improwizował;
  • [ ] Owiń odczyt konfiguracji w jedną funkcję loadConfig(), która wewnętrznie decyduje, którego parsera użyć, aby kod biznesowy nigdy nie wywoływał JSON.parse bezpośrednio;
  • [ ] Przy programowej modyfikacji konfiguracji z komentarzami używaj modify + applyEdits; nigdy nie przepisuj całego pliku;
  • [ ] Waliduj pliki dialektu również w CI (jsonc-parser dla JSONC, JSON5.parse dla JSON5), inaczej błędy składni ujawniają się dopiero w czasie wykonania;
  • [ ] Używaj standardowego JSON-a dla publicznych interfejsów, kluczy cache i ładunków kolejek komunikatów.

FAQ

Jak wybrać między JSON5 i JSONC?

Wybieraj według środowiska wykonania, a nie liczby konstrukcji składni: VS Code, TypeScript i ESLint domyślnie czytają JSONC, więc JSONC daje najlepszą zgodność; po JSON5 sięgaj tylko wtedy, gdy potrzebujesz szerszej składni literałów ES5.1, takiej jak pojedyncze cudzysłowy, klucze bez cudzysłowów lub Infinity. Żaden dialekt nie nadaje się do publicznych odpowiedzi API.

Dlaczego standardowy parser JSON nie potrafi czytać JSONC bezpośrednio?

JSONC pozwala na komentarze i przecinki końcowe, a JSON.parse akceptuje tylko JSON RFC 8259 i rzuca przy każdym z nich. Czytanie JSONC wymaga parsera takiego jak jsonc-parser albo usunięcia komentarzy przed podaniem tekstu standardowemu parserowi.

Czy komentarze w JSONC powinny przetrwać edycję pliku?

Tak, ale tylko przez API edycji. modify i applyEdits z jsonc-parser zmieniają wskazany węzeł, zachowując pozostałe komentarze; sparsowanie do obiektu i zserializowanie gubi każdy komentarz.