Home › Guides › NDJSON vs JSONL — and how to salvage a file with broken lines
NDJSON vs JSONL — and how to salvage a file with broken lines
Direct answer: there is no format difference to pick between: NDJSON and JSONL both mean one complete JSON value per line. NDJSON is the older spec name (.ndjson); JSONL / JSON Lines (.jsonl) is what logging and ML tooling settled on. The interesting problem is not the name — it is the broken lines every real file accumulates, and those are salvageable line by line with the tool below.
Salvage your line-delimited file
Each line is handled independently: valid lines pass through untouched, broken lines are repaired, hopeless lines are quarantined with their original line numbers. Processed locally in your browser — nothing is uploaded.
Line by line
| Line | Status | Rules applied / reason | Content |
|---|
One idea, two names
A line-delimited JSON file is a stream of independent documents, one per line:
{"id":1,"event":"login"}
{"id":2,"event":"click"}
{"id":3,"event":"logout"} NDJSON ("Newline Delimited JSON", ndjson.org) specified this first and allows any JSON value per line. JSON Lines (jsonlines.org) recommends objects per line and the .jsonl extension you will see across ML datasets and observability exports. In the wild the extensions are used interchangeably and every reader accepts both — if a tool claims to support one and not the other, it is describing its community, not a grammar difference.
The rules that make it work
- Each line is a complete value. No pretty-printing inside a record — the moment one record spans lines, line-based processing breaks.
- UTF-8, newline-separated. CRLF files exist (Windows tooling) and a robust reader treats a trailing
\ras framing, not content. - No enclosing array. The file is not itself JSON — wrapping it in
[ ]with commas is a different format (a JSON array) with different failure behaviour.
How files break — and what salvage does about it
| Damage | Salvage outcome |
|---|---|
| A line with single quotes / trailing comma / Python literals | Repaired per line, with the rules applied shown in the line table |
| A plain-text log line among JSON lines | Quarantined with its original line number and the reason |
| Last line truncated (writer killed mid-write) | Repaired by closure if the record is completable, otherwise quarantined — never silently kept half-parsed |
| A whole pretty-printed document pasted instead | Detected — you are pointed to JSON Repair with a one-click transfer |
| Empty lines | Skipped and counted separately — they never shift the line numbers of real records |
The discipline that matters: salvage distinguishes repaired from quarantined and keeps original line numbers stable, so when the producer asks "which records were bad?", the quarantine file is the answer — the original text, the line number, and the reason, rather than a shrug and a smaller file.
Frequently asked questions
NDJSON vs JSONL — is there any real difference?
In the data itself, no: both are one complete JSON value per line, UTF-8, lines separated by \n. NDJSON (ndjson.org, .ndjson) is the older specification; JSONL / JSON Lines (jsonlines.org, .jsonl) is the format most logging, streaming and ML tooling standardised on. Tools that read one read the other; the names signal community, not grammar.
Why use line-delimited JSON at all?
Streaming and damage control. A consumer can process record 1 without waiting for record 10,000; appending is trivial; and a corrupt line costs one record instead of the whole document — which is exactly the property the salvage tool on this page exploits.
What breaks NDJSON/JSONL files in practice?
A pretty-printed record sneaked into the stream (one record spanning lines), a log line that is not JSON at all, a writer killed mid-line (the last line is truncated), non-JSON values from a JS/Python producer on some lines, and CRLF endings from Windows tooling confusing naive splitters.