Skip to main content
UtilitiesFree · no sign-up

JSON Formatter and Validator

Pretty-print or minify JSON, with the parse error pointed at the line that broke it.

Updated

Parsing and formatting happen in your browser. Nothing is uploaded, stored, or written into the page URL, which matters when the payload is a real API response.

Formatted

{
  "tool": "json-formatter",
  "runsInBrowser": true,
  "limits": {
    "upload": null,
    "depth": {
      "reported": true,
      "counted": "arrays and objects"
    }
  },
  "tags": [
    "format",
    "minify",
    "validate"
  ]
}
Nesting depth
3
Keys
8
Objects and arrays
3 and 1
Minified size
166 B
Minifying saves
nothing, it is already minified

JSON has no comments and no trailing commas, and duplicate keys are not an error: the last one silently wins. If a payload only parses in your editor, one of those three is usually why.

In short

Why does my JSON say invalid when it looks fine?

Almost always one of three things JSON forbids and JavaScript allows: a comment, a trailing comma, or a string in single quotes. None of the three is legal JSON. This page gives you the line and column rather than a bare verdict, so the trailing comma in its own broken sample is reported at line 3, column 25.

Two more silent traps: duplicate keys are legal and the last one quietly wins, and any integer above 9,007,199,254,740,991 loses precision the moment it is parsed.

How to use the JSON formatter and validator

Paste on the left and the formatted document appears on the right, updated on every keystroke. Below it the panel reports the nesting depth, the number of keys, how many objects and arrays the document holds, its minified size, and how many bytes minifying would save against what you pasted.

Depth is the figure people underestimate. A response that reads as flat often nests four or five levels once you count every wrapper object, and deep nesting is what makes a payload hard to consume. Each array or object opens one more level, while a string, a number, a boolean and null open none.

30.0%

Saved by minifying

the sample at 2 spaces, 237 to 166 bytes

41.8%

Saved at 4 spaces

285 bytes down to 166

22.1%

Saved with tabs

213 bytes down to 166

Those three figures are the same document formatted three ways, and they answer a common argument. Pretty printing is not free, but the cost is whitespace and whitespace compresses to almost nothing. Once the response is gzipped or Brotli compressed the difference between the four versions is close to noise.

Sorting keys is the underrated switch. JSON objects have no defined order, so two servers can send the same data in different sequences and produce a diff full of moved lines. Sorting recursively before comparing collapses that noise, and it makes a payload easier to scan when you are looking for a field by name.

Nothing you paste leaves your browser. Parsing, formatting and measuring all happen on this page, with no upload, no logging, and nothing written into the page URL. Real API responses carry real customer data, and this tool is built so that pasting one here does not become a disclosure.

Do

  • Read the line and column before rereading the whole document.
  • Sort keys on both payloads before diffing two API responses.
  • Ask the API to send identifiers above 2^53 as strings.
  • Minify for the wire and pretty print for the file in your repository.
  • Match the indent your linter already enforces, whichever one that is.

Don't

  • Put comments in a file that a strict JSON parser has to read.
  • Assume a duplicate key is an error, because it parses and the last wins.
  • Trust that a very large identifier survived the round trip unchanged.
  • Paste a production response containing customer data into an online validator.
  • Count on trailing commas because your editor and your bundler tolerate them.

The mistakes that stop a JSON document parsing, what the parser calls each one, and the fix.

What you wroteLegal JSON?What the parser calls itThe fix
A // or /* */ commentNoExpected property name or '}'Remove it, or use JSONC if the reader supports it
Trailing comma in an objectNoExpected double-quoted property nameDelete the last comma
Trailing comma in an arrayNoUnexpected token ']'Delete the last comma
Single-quoted stringNoExpected property name or '}'Use double quotes everywhere
Unquoted key {a: 1}NoExpected property name or '}'Quote every key
NaN or InfinityNoUnexpected token 'N' or 'I'Send null, or a string, and decide at the receiver
Leading zero, as in 01NoUnexpected numberWrite 1, or quote it if it is a code
.5 or 5. as a numberNoUnterminated fractional numberWrite 0.5 and 5.0
A raw newline inside a stringNoBad control character in string literalEscape it as backslash n
Duplicate keysYesnothing at all, it parsesNothing to fix, but the last value silently wins
Integer above 2^53 − 1Yesnothing at all, it parsesHave the API send it as a string
The wording in the third column comes from the browser own JSON parser and varies a little between browser versions. The line and column this page reports are computed here and do not vary.

What JSON forbids that JavaScript allows

JSON started as a subset of JavaScript and stayed small on purpose. The grammar fits on one page, which is why there is a parser for it in every language. Everything convenient that JavaScript added afterwards, and everything a developer reaches for out of habit, is outside that grammar.

JSON is a lightweight, text-based, language-independent syntax for defining data interchange formats.
ECMA-404, The JSON Data Interchange Syntax, 2nd edition, December 2017
  • No comments of either kind, which is the most common complaint about the format.
  • No trailing comma after the last element of an array or the last member of an object.
  • Strings use double quotes only, and every key must be quoted.
  • No NaN, no Infinity, no undefined, and no leading zero on a number.
  • A number needs a digit on both sides of the point, so .5 and 5. are both invalid.

The formats that relax those rules are real but separate. JSONC adds comments and is what editors use for configuration files. JSON5 adds comments, trailing commas, unquoted keys, single-quoted strings and hexadecimal numbers. Neither is JSON, and a strict parser at the other end of an API will reject both without apology.

The practical rule is to know which one you are writing. A configuration file your own tooling reads can be JSONC. Anything crossing a network boundary should be plain JSON, because you do not control the parser on the other side and the tolerant behaviour you are relying on is not in the specification.

The two failures that do not raise an error

A syntax error is the easy case, because something tells you. The two problems worth learning are the ones where the parser succeeds, hands you a value, and never mentions that the value is not what the sender wrote.

What the parser does without telling you
Duplicate key {"a":1,"a":2}
parses, and the result is {"a":2}
What the specification says
behaviour is not defined, so parsers differ
Integer 9007199254740993
parses as 9007199254740992
Safe whole number ceiling
9,007,199,254,740,991, which is 2^53 − 1

Both are legal documents. Neither raises an error anywhere. The duplicate key count on this page collapses along with the parser, so a key count lower than you expected is itself a hint that duplicates were present.

Duplicate keys matter most where documents are merged or generated by templating, because that is where the same key gets emitted twice without anyone intending it. RFC 8259 calls the behaviour undefined and warns that implementations differ, so a payload that works against one server can behave differently against another.

The number problem is bigger and more common. Database identifiers, snowflake IDs and timestamps in nanoseconds all routinely exceed 2^53 − 1, and every one of them loses its last digits in a JavaScript parser. The only real fix is at the source: send the value as a string and parse it deliberately.

Carrying binary through a JSON field

JSON has no byte type, so a file or a key inside a payload has to travel as text. Base64 is the usual answer, and the size cost is worth checking first.

Open the Base64 encoder

The formula, worked line by line

There is no arithmetic in formatting, only counting, so the definitions matter more than any calculation. Depth, keys and size are the three figures this page reports, and each of them is defined below exactly as the tool computes it rather than as a rule of thumb.

The error location is the one genuine computation. A parser reports a character offset into the document, which is useless to a person reading 400 lines. Turning that offset into a line and a column is a matter of counting the newlines before it, and that is the whole trick.

depth = deepest nesting of arrays and objects, a plain value is 0
keys = every object key at every level, after duplicates collapse
bytes saved = input bytes − minified bytes
error line = 1 + the number of newlines before the offending character
error column = offset − start of that line + 1
Nesting depth in a JSON documentThe sample document nests 3 levels deep: the document itself, the limits object inside it, and the depth object inside that. It holds 8 keys across 3 objects and 1 array. Pretty printed at 2 spaces it is 237 bytes and minified 166, so minifying strips 30 percent of it back off.DEPTH 3 · 8 KEYSthe document4 keys"limits"2 keys"limits" then "depth"2 keys123every array or object opens one more level; a string or number opens noneTHE SAMPLEpretty printed237 Bminified166 Bobjects and arrays3 + 1minifying saves30%
The sample document nested three levels deep, with its key count and the bytes minifying removes.
The sample document, four ways
Minified
166 bytes
Indented with a tab
213 bytes, 22.1 percent larger
Indented with 2 spaces
237 bytes, 30.0 percent larger
Indented with 4 spaces
285 bytes, 41.8 percent larger
Depth and keys
3 levels, 8 keys

The same 8 keys across 3 objects and 1 array. All four versions parse to exactly the same value, which is the point: indentation is for the reader and carries no meaning at all.

Depth counts containers, not values. A single number is depth 0, an empty object is depth 1, and an object holding an object holding an object is depth 3. That last one is the sample above, whose limits object contains a depth object, and it is why the figure is a useful proxy for how awkward a payload will be to consume.

Byte counts here are UTF-8 bytes rather than characters, because that is what a network and a size limit actually measure. A document full of non-Latin text is larger than its character count suggests, and an emoji in a string is four bytes even though it looks like one character on screen.

Minification saves whitespace and nothing else, so its value depends entirely on whether the response is compressed. Over a gzipped or Brotli connection the saving shrinks to very little, because repeated runs of spaces are exactly what a compressor removes best. Uncompressed, in a log line or an embedded string, the figures above are the real ones.

Questions people ask

Sources

Where the constants and formulas on this page come from. Each line names the figure it backs.

  1. Behaviour is unpredictable when object names are not unique, and integers outside plus or minus 2 to the 53rd are not interoperable across implementations.

    RFC 8259: The JavaScript Object Notation (JSON) Data Interchange FormatIETF, 2017

  2. The JSON syntax itself, defined independently of any programming language.

    ECMA-404: The JSON data interchange syntax, 2nd editionEcma International, 2017