What JSON is, and where it trips people up

Updated 2026-08-28 ยท about 8 minute read

JSON is the format most data on the internet travels in. It is small, readable, and supported everywhere โ€” and it has a handful of sharp edges that cause real bugs, most of them in places people do not look.

What JSON is

JSON stands for JavaScript Object Notation. It was specified by Douglas Crockford in the early 2000s, based on JavaScript's object literal syntax, and it displaced XML for most purposes within a decade for one simple reason: it is far less to type and far easier to read.

It is a text format. A JSON document is a string, which is why it can travel in an HTTP body, sit in a file, or be stored in a database column without any special handling.

{
  "name": "Pikkit",
  "tools": 87,
  "free": true,
  "categories": ["emoji", "text", "calc"],
  "owner": null
}

That example contains one of every type JSON has, which is a short list.

The six types, and only six

  • string โ€” always in double quotes
  • number โ€” integer or decimal, no distinction between them
  • boolean โ€” true or false, lowercase
  • null
  • array โ€” ordered, in square brackets
  • object โ€” key/value pairs in curly braces, keys always strings

That is the whole type system. There is no date, no binary, no integer-versus-float distinction, no comments, no undefined, no infinity, and no NaN. Everything else has to be encoded as one of the six, and most JSON problems come from that squeeze.

Binary is the clearest case: to put an image in JSON you must encode it as text, which means Base64, which costs a third more bytes. Our guide to what Base64 is explains why.

Why your JSON will not parse

Almost every parse error is one of these five.

  • A trailing comma. {"a": 1,} is invalid. JavaScript allows it, JSON does not, and this is far and away the most common error.
  • Single quotes. {'a': 1} is invalid. JSON strings and keys require double quotes, always.
  • Unquoted keys. {a: 1} is valid JavaScript and invalid JSON.
  • Comments. There are none. // and /* */ both fail. Crockford removed them deliberately, on the grounds that people were using them to carry parsing directives.
  • Unescaped characters in strings. Literal newlines, tabs and double quotes must be escaped as \n, \t and \". Copying a multi-line string straight into a JSON file breaks it.

Parser error messages are usually unhelpful โ€” "unexpected token" at a position that is after the real mistake, because the parser only notices when something downstream fails to fit. The JSON formatter points at the actual problem and pretty-prints valid input so nesting becomes visible.

Two subtler rules. Duplicate keys are not an error โ€” the specification does not forbid them, and most parsers silently keep the last one, so a typo can shadow a real value with no warning. And object key order is not guaranteed to be preserved by every parser, so never rely on it.

The large-number problem

This one silently corrupts data, which makes it the most dangerous item here.

JSON's specification places no limit on the size or precision of a number. JavaScript, however, parses every number into a double-precision float, which represents integers exactly only up to 253 โˆ’ 1 โ€” that is 9,007,199,254,740,991.

Send a 19-digit identifier โ€” a Twitter/X post ID, a Discord snowflake, a database bigint โ€” and JavaScript will parse it, round it, and hand you a number that is quietly a few units wrong. No error, no warning. The record you then fetch is the wrong record.

The fix is to transmit large identifiers as strings. Every major API that has been bitten by this now does exactly that, which is why so many return "id": "1234567890123456789" rather than a bare number.

The same rounding affects decimals, for the ordinary floating-point reason that 0.1 + 0.2 is not 0.3. Never store money as a JSON number. Use minor units as an integer โ€” 1250 for ยฃ12.50 โ€” or a string.

There is no date type

Dates have to be encoded as strings or numbers, and the choice matters.

The convention worth following is ISO 8601 in UTC: "2026-08-28T14:30:00Z". It sorts correctly as plain text, it is unambiguous, and every language can parse it. The trailing Z means UTC.

The alternatives are worse. "28/08/2026" is ambiguous โ€” August 28th to a British reader, and invalid to an American one who reads it as month 28. A Unix timestamp is unambiguous but unreadable, and you have to know whether it is in seconds or milliseconds; the timestamp converter handles both. A date with no time zone is meaningless the moment it crosses a border, which our guide to working across time zones covers.

JSON, JSONL and JSON5

JSON Lines (.jsonl) puts one complete JSON object on each line, with no wrapping array. This makes a file streamable โ€” you can process a hundred-gigabyte log line by line without holding it in memory โ€” and appendable, since adding a record means writing one more line rather than rewriting a closing bracket. It is the standard for logs and data pipelines.

JSON5 adds back the things people miss: comments, trailing commas, unquoted keys, single quotes. It is pleasant for configuration files and is not interchangeable with JSON, so do not send it over an API.

NDJSON is effectively the same idea as JSON Lines under a different name.

And a security note that still catches people: never evaluate JSON as code. Use a real parser. JSON.parse() exists precisely so that untrusted input cannot execute, and reaching for eval() hands an attacker your application.

Pikkit has the rest of the developer tools โ€” UUIDs, URL encoding, regex โ€” and Web & Encoding collects them.

Try the tool

Frequently asked questions

Can JSON have comments?

No. Comments were deliberately excluded from the specification. If you need them for a config file, use JSON5 or YAML, which are not interchangeable with JSON.

Why does my JSON say 'unexpected token'?

Usually a trailing comma, single quotes instead of double, or an unquoted key. The reported position is often after the real mistake, since the parser only fails when something downstream does not fit.

How should dates be stored in JSON?

As ISO 8601 strings in UTC, such as 2026-08-28T14:30:00Z. They sort correctly as text and are unambiguous everywhere.

Why do APIs return IDs as strings?

Because JavaScript parses numbers as doubles, which lose precision above about 9 quadrillion. A 19-digit ID sent as a number comes back silently wrong, so it is sent as a string instead.

What is the difference between JSON and JSONL?

JSONL puts one complete JSON object on each line with no wrapping array, which makes large files streamable and appendable. It is standard for logs and data pipelines.