JSON Repair
Fix trailing commas, single quotes, comments and Python literals, with a list of every change.
Nothing you paste leaves your browser. The connect-src allowlist makes that a browser guarantee rather than a promise. Check it yourself
Repair takes something that is nearly JSON and makes it valid. Trailing commas, single quotes, unquoted keys, comments, Python literals, typographic quotes, unclosed brackets, invisible whitespace.
The output is not the interesting part. The changelog is. A repair tool that silently rewrites your payload is worse than no repair tool at all, because you cannot tell what it decided on your behalf. Every change is listed with its line and column, and the ones that involved a judgement call are listed separately from the ones that did not.
What it fixes without needing to guess
These have exactly one sensible interpretation, so they are applied without comment beyond the changelog entry.
- Trailing and doubled commas
- Removed. Legal in JavaScript and JSON5, never in JSON.
- Single-quoted strings
- Converted to double quotes, with the contents re-escaped so an apostrophe inside does not break the result.
- Unquoted keys
- Quoted. JavaScript allows bare identifiers as keys; JSON does not.
- Comments
- Removed, both // and /* */ forms.
- Python literals
- True, False and None become true, false and null.
- Typographic quotes
- The curly quotes autocorrect produces become straight ASCII quotes.
- Invalid whitespace
- No-break spaces and their relatives become ordinary spaces; zero-width characters are removed.
- A byte order mark
- Stripped from the start of the document.
- Missing commas and colons
- Inserted where the grammar makes the intent unambiguous.
- Raw control characters in strings
- Escaped, so a literal newline becomes \n.
What it flags as a judgement call
These change meaning, and no default is right for everyone. They are applied, listed separately, and configurable.
- Leading zeros
- The default is to quote the value: 02134 becomes the string "02134". Leading zeros in real data are nearly always a zip code, an account number or a SKU, where dropping them destroys the value silently. If it genuinely is a number, switch the policy to strip and it becomes 2134.
- NaN, Infinity and undefined
- Converted to null, which is what most encoders substitute. Python emits NaN and Infinity from json.dumps by default; pass allow_nan=False to catch it where it starts.
- Bare words used as values
- Quoted as strings. {"status": pending} becomes {"status": "pending"}, which is usually right and occasionally not.
- Unclosed brackets
- Closed at the end of the document. If the file was truncated rather than mistyped, this produces valid JSON that is missing data, so the change is always listed.
- NDJSON
- Several complete values one per line are wrapped in an array by default. You can also keep only the first record, or leave the input alone.
What it will not do
It will not invent structure. If the input is not close to JSON, the repair fails and says which errors it could not resolve, rather than producing something that parses but means nothing.
It also runs a bounded number of passes. Each pass can uncover a problem the previous one was hiding, but the process converges rather than looping.
How to do this in code
Doing this in code, and the honest caveat that lenient parsing at the consumer usually means something upstream is producing bad JSON that will keep producing it.
py Python, dict repr
ast.literal_eval only evaluates literal structures, so unlike eval it cannot run arbitrary code.
import ast, json
# A Python dict printed with str() or print() is not JSON.
text = "{'name': 'Priya', 'active': True}"
value = ast.literal_eval(text) # safe: literals only, no eval
clean = json.dumps(value) # '{"name": "Priya", "active": true}' js JavaScript, JSON5
JSON5 is a superset with a real specification, which makes it a better target than an ad-hoc lenient parser.
import JSON5 from 'json5';
// Accepts comments, trailing commas, unquoted keys, single
// quotes, hex numbers and more.
const value = JSON5.parse(text);
const strict = JSON.stringify(value); ts TypeScript, JSONC
import { parse } from 'jsonc-parser';
// What VS Code uses for its own settings files: comments and
// trailing commas, and nothing else.
const errors: ParseError[] = [];
const value = parse(text, errors); sh Shell
# Strip comments and trailing commas with jq's relaxed reader
jq --args . <<< "$text"
# Or normalise a JSON5 file
npx json5 -c config.json5 > config.json Questions
- Will repair ever change my data?
- Only in the ways listed under judgement calls, and every one of those appears in the changelog with its line and column. Safe repairs affect syntax only. If you disagree with a judgement call, change the setting and run it again.
- Why does 02134 become a string rather than the number 2134?
- Because quoting is lossless and stripping is not. Leading zeros in real data are almost always significant, and a converter that silently turned a zip code into a smaller number would be doing exactly the kind of quiet damage this site exists to avoid. The strip option is one click away.
- It says it could not repair my document. Now what?
- Run it through the validator. Repair only handles problems with an unambiguous fix; a document with genuinely unrecoverable structure needs a human to decide what was intended.