JSON: The Spec Is Small, the Edge Cases Are Not

Numbers that lose precision, duplicate keys, and why you cannot store a date.

JSON has six types: object, array, string, number, boolean, null. There is no date, no integer, no comment and no trailing comma. Most of the difficulty people have with it comes from those absences.

Numbers are the sharp edge

JSON does not distinguish integers from floats — there is one number type, and most parsers map it to a 64-bit float. That gives exact integers only up to 253−1, which is 9,007,199,254,740,991.

Anything larger loses precision silently. A 64-bit database ID, a Twitter-style snowflake ID, or a value in satoshis will parse without error and come back changed:

{"id": 9007199254740993} → parsed as 9007199254740992

No exception, no warning, a different record. The fix is to transport large integers as strings. Every large API that learned this the hard way now does so.

Floating point brings the usual companion: 0.1 + 0.2 is not 0.3, so monetary amounts belong in integer minor units or in strings, never as JSON numbers.

There is no date type

Dates travel as strings, which means the format is a convention you have to agree on. ISO 8601 with an explicit offset — 2026-03-14T09:30:00Z — is the one to use. A bare 2026-03-14 is ambiguous about zone, and a Unix timestamp is fine but must be labelled with its unit.

Duplicate keys are undefined

{"a": 1, "a": 2} is valid JSON and the spec does not say which wins. Most parsers take the last, some take the first, some error. This has been used to smuggle values past validators that read one key while the consumer read another, so a parser that rejects duplicates is worth having on untrusted input.

Key order is not guaranteed

Objects are unordered by definition. Most implementations preserve insertion order in practice, but nothing obliges them to, and canonical serialisation for signing or hashing must sort keys explicitly — otherwise the same data produces two different digests.

What JSON is not

Parsing untrusted input

Deeply nested arrays can exhaust the stack in recursive-descent parsers, and a large document can exhaust memory before any of your validation runs. Limit body size at the edge, and prefer a parser with a configurable depth limit. Never hand untrusted JSON to an evaluator — eval on JSON-shaped text is remote code execution with extra steps.

Formatting for humans and machines

Pretty-printing with two-space indentation is for reading and for diffs. Minified is for the wire, and the difference on a large payload is substantial — though gzip narrows it considerably, so minifying a response that is already compressed buys less than people assume.

Format and validate JSON → · Related: Unix time · Back to all articles