Skip to main content
CuteJSON
↑↓ Navigate Enter Select Esc Close

How to format JSON without losing precision

Published 2 min read By Akash Kinkar Pandey

Formatting JSON sounds like the simplest thing a tool can do: add some spaces and line breaks so a human can read it. Most formatters do exactly that by calling JSON.parse and then JSON.stringify with an indent argument. That round trip works for the JSON you write by hand and quietly breaks on the JSON you get from somewhere else.

The two bugs that round trip causes

JSON.parse turns every number into a JavaScript double. A double cannot hold every integer exactly past Number.MAX_SAFE_INTEGER, which is 9007199254740991. A snowflake ID, a database primary key, or a timestamp in nanoseconds can easily exceed that, and once it does, re-serializing the parsed value changes the digits. 12345678901234567890 comes back as 12345678901234567000. Nobody notices until the ID is wrong somewhere downstream.

The same round trip also reorders object keys that look like numbers, since JSON.stringify walks a JavaScript object’s own enumerable keys in a specific order: integer-like keys first, ascending, then everything else in insertion order. A document written as {"2": "b", "1": "a"} comes back as {"1": "a", "2": "b"}, which is a different document even though every value survived.

What the JSON formatter does instead

This tool never calls JSON.parse. It tokenizes your document, validates the token stream against RFC 8259, and reprints from those tokens. A number is copied as the exact characters you wrote, not re-encoded through a double. A key stays wherever you put it. Pasting

{"2":"second","1":"first","id":12345678901234567890,"note":"line1\nline2"}

and pressing Format returns:

{
  "2": "second",
  "1": "first",
  "id": 12345678901234567890,
  "note": "line1\nline2"
}

"2" stays before "1", and the twenty digit id comes back exactly as written. Only whitespace changed.

Reading a failure

If the document is not valid JSON, the tool marks the offending line in the gutter and names the actual mistake rather than quoting the token it choked on. A trailing comma before a closing brace is reported as a trailing comma, at the line the comma sits on, not at the end of the file where a naive parser gives up.

Format vs Minify

Both buttons do the same reprint from tokens; only the whitespace differs. Format adds line breaks and the indent width you chose (2 spaces, 4 spaces, or a tab). Minify strips every space, tab and line break that JSON does not require. Neither one changes a value, so it is safe to press either one as many times as you like, or switch between them without re-pasting anything.

Try it on a document with a long ID or a config file with numeric keys, and compare the result against a formatter that goes through JSON.parse. The difference shows up immediately.

Back to all posts