Core answer: JSON has exactly six types: string, number, boolean, null, object, array. Strict rules: double quotes only, no trailing commas, no comments, no undefined, no functions. Most parse errors come from five causes — single quotes, trailing commas, unquoted keys, comments, and literal Infinity/NaN. Format with 2-space indent for humans; minify for transport.

The grammar in 60 seconds

{"string": "double quotes only", "number": 42, "float": 3.14, "bool": true, "nothing": null, "object": {"nested": "yes"}, "array": [1, "two", false]}

That's the entire language. Keys are always strings. No dates, no undefined, no regexes, no functions — serialize those as strings/numbers yourself.

The five parse killers

ErrorBadFixed
Single quotes{'a': 1}{"a": 1}
Trailing comma{"a": 1,}{"a": 1}
Unquoted key{a: 1}{"a": 1}
Comment{"a": 1 /* x */}remove it
NaN/Infinity{"v": NaN}{"v": null}

Worked examples

Example 1 — API debugging. A 400 error on POST: the body had "price": 19.90, (trailing comma before }). Python's json module says "Expecting property name enclosed in double quotes: line 3 column 5" — the line:column is your flashlight.

Example 2 — Config files. package.json forbids comments, which is why JSONC (VS Code settings) and JSON5 exist — but they're NOT JSON; a strict parser will reject them. Keep build configs in strict JSON, human-edited settings in JSONC.

Example 3 — Big-integers hazard. {"orderId": 9007199254740993} parses in JS as 9007199254740992 — beyond Number.MAX_SAFE_INTEGER (2⁵³−1). IDs larger than that must be strings: {"orderId": "9007199254740993"}.

Example 4 — Streaming large files. A 2 GB JSON array won't fit in memory; use NDJSON (one object per line) and process line-by-line — that's why logs and LLM streams use it.

Common mistakes and myths

  1. JSON.stringify for deep clones — it drops undefined, functions, and converts Dates to strings; use structuredClone() in modern runtimes.
  2. Trusting parse without try/catch — user-uploaded JSON will be malformed; always handle SyntaxError gracefully.
  3. Assuming key order is guaranteed — JS preserves insertion order practically, but the spec doesn't promise it; never encode meaning in key order.
  4. Pretty-printing in production APIs — whitespace is 10–30% of payload; minify transport, pretty-print only for humans.
  5. Storing "null" as string — "null" ≠ null; validate types at the boundary (zod, pydantic) before they infect your logic.