JSON Formatter & Validator
How it works
Why 9007199254740993 comes back as 9007199254740992
JavaScript stores every JSON number as an IEEE 754 double, which has 53 bits of integer precision. Number.MAX_SAFE_INTEGER is 9007199254740991; above it, not every integer exists, so JSON.parse rounds to the nearest representable one — 9007199254740993 becomes 9007199254740992 with no error. This bites real data constantly: 64-bit database ids, Twitter/X snowflake ids, nanosecond timestamps. This tool detects integer literals that cannot round-trip and shows a warning naming the original and the rounded value. The formatted output still contains the rounded number — that is a limit of JSON.parse itself — so the fix upstream is to transmit such ids as strings.
Duplicate keys are legal, silent, and last-wins
RFC 8259 only says object names SHOULD be unique, so {"a":1,"a":2} is syntactically valid JSON. The ECMAScript spec makes JSON.parse keep the last occurrence, so the first value vanishes without any error — a classic source of config files where an edit 'does nothing' because the same key appears again lower down, and of parser-differential security bugs when another system keeps the first value instead. This tool scans for duplicate keys (comparing decoded names, so "a" and "a" count as the same key) and warns which ones were collapsed.
Errors with a real position
A bare 'Unexpected token }' is useless in a 500-line payload. When the input is invalid, this tool reports the line and column of the failure whenever the engine provides a position (Chrome and Edge do for most errors; the raw engine message is shown otherwise) — for example a trailing comma in {"a":1,} is reported at line 1, column 8. Trailing commas and comments are the usual suspects: both are fine in JavaScript object literals, JSON5 and JSONC, but hard errors in JSON.
Escaped JSON inside JSON
Logs and API payloads constantly nest JSON as a string: an LLM tool call's arguments field, a webhook's payload column, a stack trace stored in a JSON log line. What you see is {"args": "{\"city\":\"Paris\"}"} — every quote escaped, every newline a literal \n, unreadable at one level deep and hopeless at two. The UNESCAPE mode turns a JSON string literal back into the text it encodes (a pasted bare escaped body works too — surrounding quotes are optional), and tells you when the result is itself JSON so you can format it next. ESCAPE goes the other way: it turns any text into a JSON string literal, ready to embed.
Sort keys for stable diffs
Two JSON documents with the same data but different key order diff as if everything changed. The Sort keys option recursively sorts every object's keys, so formatting the before and after versions gives a minimal, readable diff. Combined with Minify it also produces a canonical-ish single line for comparing payloads.
Examples
| { "a" : 1 , "b" : [ 2 , 3 ] } | {"a":1,"b":[2,3]} |
| {"id":9007199254740993,"user":"kai"} | {"id":9007199254740992,"user":"kai"} — warns: 9007199254740993 was read as 9007199254740992 |
| {"a":1,"a":2} | {"a":2} — warns: duplicate key "a", JSON.parse keeps only the last value |
| {"b":1,"a":{"d":2,"c":3}} | {"a":{"c":3,"d":2},"b":1} (Sort keys + Minify) |
| "{\"city\":\"Paris\"}" (Unescape) | {"city":"Paris"} — notes the result is valid JSON |
| line1 line2 (Escape) | "line1\nline2" |
FAQ
Why did my large number change after formatting?
JavaScript parses every JSON number into a 64-bit float with 53 bits of integer precision. Any integer above Number.MAX_SAFE_INTEGER (9007199254740991) may not be representable, and JSON.parse silently rounds it to the nearest double — 9007199254740993 becomes 9007199254740992. This tool warns when that happens, but the output necessarily contains the rounded value. If you control the producer, send 64-bit ids as JSON strings.
Is JSON with duplicate keys valid?
Syntactically yes: RFC 8259 says object names SHOULD be unique, not MUST. Behavior is parser-defined — JavaScript's JSON.parse keeps the last occurrence, and this tool warns which keys were collapsed. Because other parsers may keep the first occurrence or reject the document, duplicate keys are an interoperability and security hazard and should be removed.
Why is a trailing comma an error?
JSON allows neither trailing commas nor comments; both are features of JavaScript object literals, JSON5 and JSONC (the VS Code settings dialect). {"a":1,} fails with the position reported as line 1, column 8 — delete the comma before the closing brace.
Does formatting change my numbers?
Representation can change, value cannot (except for the unsafe-integer rounding this tool warns about). JSON.parse turns the text 1.0 into the number 1 and 1e2 into 100, so the output shows 1 and 100. One extreme case gets a warning: a literal like 1e999 overflows to Infinity, which JSON.stringify writes as null.
How do I read JSON that is full of \" and \n?
That is a JSON string containing serialized JSON — common in LLM tool-call arguments, webhook payloads and log columns. Paste it into the UNESCAPE mode: a quoted string literal or the bare escaped content both work, and the tool tells you when the unescaped result is itself valid JSON so you can switch to FORMAT and pretty-print it.
Is my JSON uploaded to a server?
No. The page ships a strict Content-Security-Policy: connect-src 'none' blocks fetch, XHR, WebSockets and beacons, while default-src 'self' and form-action 'none' block every other way markup could send data off-site. Parsing and formatting happen in your browser and cannot leave it.