Reading and Validating JSON Without Breaking It

By Anatolie · Updated 2026-09-03

JSON (JavaScript Object Notation) is a text format for structured data. Its entire grammar is small enough to hold in your head, and nearly every "invalid JSON" error comes from a short list of habits carried over from JavaScript or Python. This guide covers the whole grammar and those failure modes.

The complete grammar

A JSON document is exactly one value. A value is one of:

  • object{ } containing zero or more "key": value pairs separated by commas. Keys must be double-quoted strings.
  • array[ ] containing zero or more values separated by commas.
  • string — double quotes only. Certain characters must be escaped with a backslash: \", \\, \/, \b, \f, \n, \r, \t, and \u followed by four hex digits.
  • number — decimal, optional minus, optional fraction, optional e/E exponent. No leading +, no leading zeros, no hex, no NaN, no Infinity.
  • booleantrue or false, lowercase.
  • null — lowercase.

Whitespace between tokens is ignored. There are no comments. That is the whole specification.

key"name"
string"Ana"
number30
booleantrue
array[1, 2]
nullnull
The six value types JSON allows — every document, however deeply nested, is built from just these.

The mistakes that cause almost every parse error

InvalidWhyFix
{ "a": 1, }Trailing comma after the last pairRemove it
{ 'a': 1 }Single-quoted key or stringUse double quotes
{ a: 1 }Unquoted keyQuote it: "a"
// note or /* */Comments are not allowedDelete, or use JSON5/JSONC deliberately
{ "a": .5 }Number with no leading digit0.5
{ "a": 1. }Trailing decimal point1.0 or 1
{ "a": undefined }undefined is not a JSON valuenull, or omit the key
{ "a": NaN }NaN/Infinity are not validUse null or a string sentinel
Smart quotes “ ”Pasted from a word processorReplace with straight "
A leading U+FEFF byte-order markSome editors add it to UTF-8 filesSave without BOM

A formatter that reports the line and column of the first error saves a lot of time here — paste the document into the JSON formatter and it will pretty-print valid input or point at the exact character that broke.

Numbers deserve special care

JSON numbers are arbitrary-precision in the spec, but most parsers load them into a 64-bit float. That means integers beyond 253 lose precision silently: an ID like 9007199254740993 may read back as ...992. If a value is an identifier rather than a quantity, transmit it as a string. The same applies to money — represent it in minor units as an integer, or as a string, not as 19.99 which is not exactly representable in binary floating point.

Duplicate keys

The spec does not forbid { "a": 1, "a": 2 } but it does not define what it means either. In practice most parsers keep the last value. Do not rely on this — deduplicate before serializing.

Validating structure, not just syntax

Syntactic validity ("is this parseable JSON") is different from structural validity ("does it have the fields my program expects, with the right types"). For the second, use JSON Schema — a JSON document that describes the shape another document must match, with keywords like type, required, properties, enum, and minimum. It is the standard way to reject bad data at an API boundary before it reaches your logic.

Related formats

  • JWT — three base64url-encoded parts, the first two of which are JSON. The JWT decoder unpacks the header and payload so you can read the claims.
  • YAML — a superset of JSON in practice, with indentation instead of braces and support for comments. The YAML ↔ JSON converter moves between the two.
  • NDJSON / JSON Lines — one JSON value per line, no enclosing array. Good for streaming and logs; not itself a single valid JSON document.