The ways a pasted JSON breaks
A document that was JSON when it left a process rarely arrives as JSON. The ways it breaks are few, and they are the same ones every time.
Where broken JSON comes from
Almost never from a JSON serialiser. It comes from a language’s own object syntax — a Python repr, a JavaScript object literal pasted out of a source file, a Ruby hash — from a config dialect that allows comments and trailing commas, from a hand edit that dropped a comma, or from a log line cut at a buffer boundary. Each origin breaks the text in a characteristic way, and each way is one mechanical fix.
The ways, by origin
Each origin leaves its own marks, and each mark is one fix — named on its line before anything changes.
- A Python repr — single quotes around strings, True, False and None, a trailing comma pprint leaves
- A JavaScript object literal — unquoted keys, single quotes, trailing commas, comments
- A config dialect — comments and trailing commas that JSON5 or JSONC allow and JSON does not
- A hand edit — a missing comma between two values, an unclosed bracket or brace
- A cut line — a document truncated at a buffer boundary, closed for you and said so
- A value no serialiser writes — undefined, NaN, Infinity — read as null, and listed
What that looks like on one file
The webhook sample above — a Stripe event printed from Python and edited by hand — needs 46 fixes in 7 kinds: 31 × quote unquoted key, 7 × remove trailing comma, 3 × single → double quotes, 2 × python false → json false, 1 × insert missing comma after value, 1 × python true → json true, 1 × python none → json null. Fix all applies them, greens the lines it touched, and leaves every other byte as pasted; Undo puts the original back exactly.
What no step does: guess at content. A value that is missing stays missing and is listed as a problem with its line. Fixing syntax is mechanical; inventing data is not, and the list is where the two are kept apart.