JSON Toolkit

Čtení hranic gramatiky JSON z RFC 8259

Shrnutí: Převod gramatických produkcí RFC 8259 do srozumitelného jazyka – které znaky smějí být přímo, které musí být escapované a kde specifikace záměrně ponechává prostor.

Začátečník~11 min čteníPublikováno 2026-10-30Aktualizováno 2026-10-30
Titulní obrázek článku Čtení hranic gramatiky JSON z RFC 8259

Stručně: když se objeví syntaktická chyba JSON, najděte první neplatný znak a zařaďte ho jako jednoduchou uvozovku, koncovou čárku, komentář nebo řídicí znak, místo abyste znovu četli celý dokument; na straně kódování ověřte UTF-8 bez BOM a celá čísla nad 2^53 - 1 přenášejte jako řetězce.

Vlastní text specifikace má jen několik stránek, přesto se za každým „MUST“, „SHOULD“ a „MAY“ skrývá incident, který kdysi držel tým vzhůru celou noc.

1. Jak souvisí tři texty

Při učení JSON často mate tři čísla: RFC 4627, RFC 7159, RFC 8259 a k tomu ECMA-404. Vztah je přitom celkem jasný:

Text Datum Stav Klíčový rozdíl
RFC 4627 2006 Nahrazen Nejvyšší úrovní musí být objekt nebo pole
RFC 7159 2014 Nahrazen Nejvyšší úroveň uvolněna na libovolnou hodnotu JSON
RFC 8259 2017.12 Platný standard Výslovně UTF-8, doplněny pokyny k interoperabilitě
ECMA-404 2013, druhé vydání 2017.12 Gramatická specifikace Definuje pouze gramatiku, neřeší kódování ani přenos

Který si přečíst? Stačí RFC 8259. ECMA-404 je v gramatické části ekvivalentní; rozdíl je v tom, že RFC 8259 navíc určuje „přenos v UTF-8“ a „problémy interoperability, na které mají implementátoři dbát“.

2. Šest typů hodnot

Gramatické produkce specifikace lze zhuštit do jednoho diagramu:

JSON-text  = ws value ws
value      = object / array / number / string / "true" / "false" / "null"
object     = "{" [ member *( "," member ) ] "}"
member     = string ws ":" ws value
array      = "[" [ value *( "," value ) ] "]"

Podstatných je jen šest bodů:

  1. Objekty používají složené závorky, členy se oddělují čárkou a čárka za posledním členem není povolena;
  2. Pole používají hranaté závorky, prvky se oddělují čárkou a koncové čárky rovněž nejsou povoleny;
  3. Čísla nepovolují úvodní +, úvodní nuly (01 je neplatné), samotnou desetinnou tečku (1. je neplatné) ani NaN nebo Infinity;
  4. Řetězce musí používat dvojité uvozovky; jednoduché uvozovky jsou neplatné;
  5. Booleany a null jsou literály s malými písmeny; True i NULL jsou neplatné;
  6. Mezi strukturálními prvky smějí být pouze prázdné znaky 0x20 (mezera), 0x09 (tabulátor), 0x0A (posun řádku) a 0x0D (návrat vozíku).

Následující úryvek je platný JSON, i když mnoho lidí při prvním pohledu předpokládá opak – je to totiž samotný řetězec na nejvyšší úrovni:

"just a string"

Od RFC 7159 může být nejvyšší úrovní skutečně libovolná hodnota. V API se však stále doporučuje používat pouze objekty nebo pole; důvod najdete v části 6.

3. Co může skutečně být přímo uvnitř řetězce

Toto je část, kde nejsnáze klame „u mě to běží bez problému“. Pravidla specifikace pro řetězce jsou:

  • Znaky, které smějí být přímo: %x20-21, %x23-5B, %x5D-10FFFF;
  • Jinými slovy, dvojitou uvozovku " (0x22) a zpětné lomítko \ (0x5C) je nutné escapovat;
  • Všechny řídicí znaky pod 0x20 je nutné escapovat, včetně posunu řádku, tabulátoru a návratu vozíku;
  • K dispozici jsou pouze escape sekvence \", \\, \/, \b, \f, \n, \r, \t a \uXXXX.
{
  "ok": "Čínský text, emoji 🎉 i celošířkové symboly lze psát přímo",
  "escaped": "Sem patří zalomení řádku\na zde končí tabulátor\t",
  "control": "\u0007 je zvonek a musí být escapován",
  "quote": "Řekl: \" Nepoužívejte jednoduché uvozovky \""
}

\/ je historická escape sekvence (brání tomu, aby </script> uzavřel skript příliš brzy). Moderní scénáře ji potřebují zřídka, ale parsery ji musí podporovat.

4. Kódování: UTF-8, žádný BOM

Část 8.1 RFC 8259 říká tři věci:

  1. Text JSON určený k výměně dat musí být kódován v UTF-8;
  2. Implementace nesmějí přidávat BOM (EF BB BF) na začátek textu;
  3. Parsery smějí BOM ignorovat, aby byly robustnější.

Dvě typické poruchy v praxi:

  • Windows Poznámkový blok ukládá jako „UTF-8 s BOM“, soubor pak JSON.parse odmítne s Unexpected token;
  • Čínský soubor JSON uložený v GBK se v pipeline, která dekóduje pouze UTF-8, změní v rozsypaný text, přitom syntaxe náhodou projde, a chyba se tak odloží do obchodní vrstvy.

Zpracování je prosté: při čtení souboru odstraňte BOM a poté explicitně dekódujte jako UTF-8; při zápisu zajistěte, aby se BOM nepřidal.

/**
 * Odstraní UTF-8 BOM na začátku řetězce.
 * @param text Původní text
 * @returns Text bez BOM
 */
function stripBom(text: string): string {
  return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
}

Ještě poznámka k mediálnímu typu: application/json už nepřijímá parametr charset; jeho uvedení nevyvolá chybu, ale jde o zastaralou praxi.

5. Tři místa, kde specifikace záměrně ponechává prostor

Právě tam, kde se specifikace zdráhá něco přesně určit, vznikají rozdíly mezi implementacemi.

1. Rozsah a přesnost čísel

RFC 8259 pouze říká, že „tato specifikace neomezuje rozsah ani přesnost čísel“, a poznamenává, že většina implementací používá dvojitou přesnost IEEE 754. Znamená to, že stejné 9223372036854775807 se v JavaScriptu stane 9223372036854776000, zatímco v Javě typu long zůstane přesné.

Závěr: celá čísla nad 2^53 - 1 a peněžní částky vyžadující mnoho desetinných míst vždy přenášejte jako řetězce a ve schématu explicitně zapište pattern.

2. Duplicitní klíče

Specifikace říká, že „názvy uvnitř objektu by měly (SHOULD) být jedinečné“; neříká, co dělat, když nejsou.

  • JSON.parse ponechá poslední;
  • některé parsery ponechají první;
  • některé knihovny prostě vyhodí chybu.

Závěr: nepište duplicitní klíče a ve validátorech se nespoléhejte na žádné konkrétní chování. Přidat při sestavení krok „detekce duplicitních klíčů“ je mnohem levnější než pozdější vyšetřování.

3. Limity hloubky a délky

Specifikace nestanoví žádný limit hloubky vnoření, ale každá implementace nějaký má. V8 při desítkách tisíc úrovní vnoření vyhodí RangeError: Maximum call stack size exceeded. Servery musí zavést vlastní limity; jde o bezpečnostní požadavek, nikoli požadavek specifikace.

6. Ověřte porozumění na protipříkladech

Každý řádek níže je neplatný JSON. Vložte je do parseru a podívejte se, zda jsou chybové zprávy užitečné:

{ 'name': 'json' }        // jednoduché uvozovky
{ name: "json" }          // název klíče bez uvozovek
{"a": 1,}                 // koncová čárka
[1, 2, 3,]                // koncová čárka v poli
{"a": 01}                 // úvodní nula
{"a": +1}                 // úvodní znaménko plus
{"a": .5}                 // chybějící celá část
{"a": NaN}                // ne‑standardní literál
{"a": undefined}          // není hodnota JSON
{"a": 1} // comment       // komentář

Zajímavý hraniční případ: {"a": 1} následované komentářem je platné v JSON5, platné v JSONC a neplatné ve standardním JSON. Takže „konfigurační soubor běží v pořádku, ale API ho odmítne“ je téměř vždy problém dialektu.

7. Produkční kontrolní seznam

  • [ ] Veškerý JSON přenášený ven používá application/json bez charset;
  • [ ] Pipeline pro čtení i zápis souborů odstraňuje BOM a používá explicitní UTF-8;
  • [ ] Celá čísla nad 2^53 jsou přenášena jako řetězce a označena ve schématu;
  • [ ] Duplicitní klíče jsou detekovány při sestavení;
  • [ ] Server omezuje velikost těla požadavku a hloubku vnoření;
  • [ ] Hodnoty na nejvyšší úrovni veřejných API jsou objekty nebo pole, nikdy samostatné skaláry.

Časté dotazy

Jak rychle zjistit, zda je text platný JSON?

Předložte text parseru nebo online nástroji pro parsování, přečtěte pozici prvního neplatného znaku a zkontrolujte ji proti šesti typům hodnot a pravidlům pro prázdné znaky z RFC 8259. Nejvyšší úrovní může být libovolná hodnota JSON, ale mezi strukturálními prvky smějí být pouze mezera, tabulátor, posun řádku a návrat vozíku. Jednoduchá uvozovka, klíč bez uvozovek, koncová čárka nebo komentář znamená, že nejde o standardní JSON.

Proč musí JSON používat UTF-8 a nesmí obsahovat BOM?

Část 8.1 RFC 8259 vyžaduje, aby text JSON určený k výměně dat byl kódován v UTF-8, říká, že implementace nesmějí přidávat BOM na začátek, a umožňuje parserům BOM kvůli robustnosti ignorovat. Soubor s BOM JSON.parse odmítne s Unexpected token, i když na pohled nevypadá nic špatně. Při čtení tedy BOM odstraňte a dekódujte explicitně jako UTF-8.

Mám použít JSON5 nebo JSONC, abych tato gramatická omezení obešel?

Ano, ale pouze pro místní čtení, například konfiguračních souborů; data vyměňovaná mezi systémy by měla zůstat standardním JSON. Protipříklady ukazují, že {"a": 1} následované komentářem je platné v JSON5 a JSONC, ale neplatné ve standardním JSON, a proto je „konfigurační soubor běží, ale API ho odmítne“ téměř vždy problém dialektu. Při převodu nejprve odstraňte komentáře, jednoduché uvozovky a koncové čárky a výsledek předejte standardnímu parseru.