Jak se číslo zapisuje
Číslo v JSON je vždy desítkové. Má celou část, před kterou smí stát mínus,
volitelnou desetinnou část za tečkou a volitelný exponent za písmenem e nebo
E. Úvodní nuly, znaménko plus před celou hodnotou, desetinná čárka ani šestnáctkový zápis povolené
nejsou. Plus je naopak povolené u exponentu, například v 1e+2. Gramatika z RFC 8259:
number = [ minus ] int [ frac ] [ exp ]
int = zero / ( digit1-9 *DIGIT )
frac = decimal-point 1*DIGIT
exp = e [ minus / plus ] 1*DIGIT Ukázky ověřené parserem:
| Zápis | Platné číslo JSON? | Proč | Nástroj |
|---|---|---|---|
| 0 | ano | nula | Vyzkoušet |
| -12 | ano | záporné celé číslo | Vyzkoušet |
| 3.14 | ano | desetinná tečka | Vyzkoušet |
| 1e3 | ano | exponent, e i E | Vyzkoušet |
| -2.5E-4 | ano | exponent se znaménkem | Vyzkoušet |
| 012 | ne | úvodní nula | Vyzkoušet |
| +1 | ne | znaménko plus před číslem | Vyzkoušet |
| .5 | ne | chybí číslice před tečkou | Vyzkoušet |
| 1. | ne | chybí číslice za tečkou | Vyzkoušet |
| 3,14 | ne | desetinná čárka | Vyzkoušet |
| 0x1F | ne | šestnáctkový zápis | Vyzkoušet |
| NaN | ne | NaN a Infinity v JSON nejsou | Vyzkoušet |
NaN a Infinity v JSON nejsou
Hodnoty, které gramatika nepopisuje, tedy NaN, Infinity a
-Infinity, v JSON nejsou povolené. Knihovny se s nimi při zápisu vypořádají
různě; záleží i na nastavení knihovny:
| Jazyk | Zápis NaN nebo Infinity do JSON |
|---|---|
| JavaScript | JSON.stringify({ x: NaN }) tiše vrátí {"x":null} |
| Python | json.dumps(float("nan")) vrátí NaN, tedy neplatný JSON; s allow_nan=False vyhodí ValueError |
| PHP | json_encode(NAN) vrátí false a chybu „Inf and NaN cannot be JSON encoded“ |
Python tak vyrobí soubor, který JavaScript ani PHP nenačtou, a JavaScript nenápadně ztratí
informaci. Pokud potřebuješ chybějící nebo neplatnou hodnotu, použij null
nebo samostatný příznak, třeba "stav": "nezmereno".
Přesnost: hranice 253
Specifikace přesnost ani rozsah čísel neurčuje a dovoluje parserům stanovit si vlastní limity. Většina knihoven ale převádí čísla na double (IEEE 754 binary64), a RFC 8259 proto uvádí rozsah celých čísel, na jejichž přesné hodnotě se tyto implementace shodnou: ±(253 − 1), tedy do 9 007 199 254 740 991. I některá větší celá čísla lze v double zapsat přesně, ale ne všechna sousední čísla. Ukázky rozdílů mezi parsery (Node.js 25, Python 3.14 a PHP 8.4):
| Vstup | JavaScript | Python | PHP |
|---|---|---|---|
| 9007199254740993 | 9007199254740992 | 9007199254740993 | 9007199254740993 |
| 1E400 | Infinity | inf | INF |
| NaN | chyba | nan | chyba |
JavaScript číslo 9007199254740993 potichu zaokrouhlí, Python a PHP ho uloží jako
celé číslo přesně. PHP na 64bitové platformě má ale strop 64bitového celého čísla; nad ním převádí na
float, pokud nepoužiješ JSON_BIGINT_AS_STRING:
JSON.parse('{"id": 9007199254740993}').id; // 9007199254740992 (o 1 méně)
Number.MAX_SAFE_INTEGER; // 9007199254740991
0.1 + 0.2; // 0.30000000000000004 json_decode('12345678901234567890'); // float(1.2345678901234567E+19)
json_decode('12345678901234567890', false, 512, JSON_BIGINT_AS_STRING); // "12345678901234567890"
Číslo zapsané s exponentem nebo desetinnou tečkou navíc Python i PHP vrátí jako desetinné:
1e2 je v obou 100.0, v JavaScriptu 100. Typ výsledku
tedy závisí i na tom, jak je číslo zapsané.
Velká ID a peněžní částky jako řetězce
Pro hodnoty, u kterých záleží na každé číslici, doporučuje profil I-JSON (RFC 7493) zápis jako řetězec. Typicky jde o 64bitová ID z databáze, čísla účtů nebo částky, u kterých nesmí vzniknout zaokrouhlovací chyba:
{
"id": "9007199254740993",
"castka": "1499.90",
"mena": "CZK"
} Příjemce musí znát význam řetězce. Částku může načíst do přesného desetinného typu; identifikátor obvykle ponechá jako text. Pravidla má uvádět dokumentace a schéma API. Druhou možností u částek je celé číslo v nejmenší jednotce (haléře, centy). Pokud musíš desetinné číslo načíst přesně, některé knihovny to umí přímo:
import json
from decimal import Decimal
data = json.loads('{"castka": 1499.90}', parse_float=Decimal)
data['castka'] # Decimal('1499.90'), přesně Doporučené postupy
- Celá čísla nad 9 007 199 254 740 991 a ID z databáze posílej jako řetězce.
- Peněžní částky přenášej jako řetězec, nebo jako celé číslo v haléřích či centech.
- Nikdy nezapisuj
NaNaniInfinity; v Pythonu zapniallow_nan=False, ať se chyba projeví hned. - Nespoléhej na rozdíl mezi
1a1.0; jako typ ho nemusí zachovat žádný parser. - Rozsah a formát čísel popiš v JSON Schema (
type,minimum,maximum,multipleOf).