CSV to JSON Converter
Convert CSV to JSON or JSON to CSV — streaming to 100 MB, type-safe by default — entirely on this device.
Converts numbers, true/false and null. Guarded: leading-zero codes (00501) and integers beyond 2^53 stay strings.
Press ? for shortcuts
Do it offline:$ mlr --icsv --ojson cat data.csv
How to use it
- Paste CSV or JSON into the input pane, or drop a .csv, .tsv, .txt or .json file on it — the direction badge names what was detected, and one click on the badge overrides the guess.
- Set the options that matter: header row auto, on or off; output shape (row objects, arrays, keyed or NDJSON); delimiter; and the opt-in Detect types switch with its guards.
- Read the row and column census in the status line, then open the Table tab to inspect the parsed grid before you trust the conversion.
- Copy the output, download it as .json, .ndjson or .csv, or share the tool state as a link — credential-shaped input is kept out of the browser's storage, and the page says so.
A CSV file and a JSON document are two spellings of the same table. Paste or drop either one: CSV becomes row objects, arrays, a keyed map or NDJSON, and JSON flattens back into RFC 4180 CSV. Delimiter, header row and direction are detected automatically, every cell stays a string until you ask otherwise, and files up to 100 MB stream through this device without an upload.
How it works
Every parse produces a typed table model — column names, padded data rows, the delimiter with its provenance, and the census the status line prints. The reader is PapaParse, wrapped so the delimiter auto-detects across comma, semicolon, tab and pipe (a tab-paste out of Excel lands as TSV), a header row is recognized conservatively rather than guessed, and mixed line endings are normalized outside quoted fields before parsing begins.
CSV's grammar is smaller than its reputation. RFC 4180 quotes a field when it holds the delimiter, a quote or a line break, and spells a literal quote by doubling it — which is why a field can legally contain a real newline, and why splitting text on line breaks corrupts row counts. The same grammar drives the writer in the JSON to CSV direction: cells quote only where required, and records join with CRLF unless you switch to LF.
Detect types is off on purpose. Coercing cell text to numbers is what turns the ZIP code 00501 into 501 and rounds identifiers past 2^53 — so by default every cell survives as a string, byte for byte. Switched on, detection converts clean number spellings, true, false and null, while two guards hold back leading-zero digit strings and integers beyond ±2^53; each guarded cell is named in a note so the exception is loud.
Small inputs convert as you type, larger ones move to a worker, and a big file is read in chunks: parsed rows feed a sink that emits output fragments, the fragments accumulate as a Blob, and for a well-formed table the assembled result is byte-identical to a buffered run — the full output never needs to exist as one string. Two streamed cases diverge by design: keyed output writes duplicate keys as repeated members (parsers keep the last), and rows wider than the detected columns are clipped to the census, with the clipped rows counted and reported. JSON to CSV is the honest exception, because JSON.parse needs the whole document; past 100 MB the tool refuses and points at the command line instead.
Examples
name,zip
Ana,00501[{"name":"Ana","zip":"00501"}]Detect types is on, yet the ZIP stays quoted — the guard reports: row 1, column "zip": "00501" kept as a string — a leading zero would be lost as a number. Turning 00501 into 501 is how postal codes break in pipelines; here the exception is named instead of silent.
name;total
Ana;1,5[{"name":"Ana","total":"1,5"}]No option named the delimiter — the vote across comma, semicolon, tab and pipe picked the semicolon, the layout Excel writes in locales where 1,5 means one and a half. The cell survives as the text 1,5; a comma decimal is never mistaken for a JSON number.
id,note
1,"Fix header
then re-run"
2,Done[{"id":"1","note":"Fix header\nthen re-run"},{"id":"2","note":"Done"}]Three text lines, two records: the quoted note carries a real line break, so the census reads 2 rows · 2 columns. Naive newline splitting would invent a third, broken row — quote-aware reading is the entire reason to use a CSV parser.
[{"name":"Ana","role":"admin"},{"name":"Iker","city":"Bilbao"}]name,role,city
Ana,admin,
Iker,,Bilbao
The objects disagree on keys, so the header row is their first-seen union and the warning spells it out: objects carry different keys — columns are the union of all 3 keys; cells a row is missing are left empty. Records end with CRLF because RFC 4180 says so; a toggle switches to LF.
id,event
9007199254740993,login
9007199254740993,logout{"id":"9007199254740993","event":"login"}
{"id":"9007199254740993","event":"logout"}
NDJSON emits one compact object per line with no enclosing array, which is why log pipelines favor it for large exports. The id exceeds 2^53, so Detect types keeps it a string — as a JSON number, 9007199254740993 becomes 9007199254740992, an identifier changed by one.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
A byte-order mark never becomes part of the first header
Excel and PowerShell like to prepend U+FEFF to UTF-8 files; read naively, it glues itself onto the first column name. Here the mark is stripped and a note says so. On the way out, the BOM switch prepends one again — that is the entire meaning of the Excel-compatible hint on that option.
Duplicate headers rename; nothing merges silently
Two columns both named email cannot become two identical JSON keys — one value would overwrite the other without a trace. The later column is renamed email_2, both values survive, and a warning records the rename, because silent repair is the failure mode this tool refuses to have.
Ragged rows pad to the census and are listed by number
A row with fewer cells than the widest one is padded with empty cells so every object still carries every key. The warning lists up to 20 short data rows by number and then states exactly how many more exist — a countable tail, not an ellipsis.
Mixed line endings resolve outside quotes only
A file stitched together from Windows and Unix sources can mix CRLF, LF and bare CR. Every ending outside quoted fields is read as a row separator and a chip reports which kinds were mixed; a line break inside a quoted field is data and stays byte-untouched.
Windows-1252 exports are suspected, not silently mangled
When bytes fail UTF-8 validation, the likely truth is a legacy Excel export — é arriving as é, ñ as ñ. The file path replaces the offending bytes visibly, warns that the encoding looks like Windows-1252, and offers a one-click re-decode; the choice stays yours.
Keyed output states its duplicate-key rule
The keyed shape indexes rows by one column's values. Two rows sharing a key cannot both survive as members, so the last row wins and a warning names the key and the row that won; a key column that does not exist refuses outright, listing the columns that were actually parsed.
Common errors
Row 2: quoted field opened at column 5 never closes — this usually breaks every following row; close the quote, or write quotes inside a field as "".- The row and column point at the quote that opened the never-closed field, and the frame under the message shows it in context. Everything after that quote reads as one endless cell — fix the first reported row, and the rows after it usually come back on their own.
Top-level value is a single object, not an array of rows — wrap it in an array: [ { … } ].- Rows are the unit of CSV, so the top level must be an array: objects become rows with their keys as columns, inner arrays become bare rows of cells. A lone object is one row's worth of data — wrapped in brackets, it converts immediately.
duplicate header "email" — renamed to "email_2" so no column is lost- A rename is the only outcome that loses nothing: both columns keep their data and the JSON keys stay distinct. If the duplicate is accidental, fix the source header; if it is intentional, the _2 suffix is predictable and stable.
1 of 2 rows have a different number of cells than the 3-column census — short rows were padded with empty cells (rows 1)- Data rows are numbered from 1, matching the census and the Table tab. Padding keeps object shapes consistent for downstream code; when short rows are a surprise, the listed numbers say exactly where to look in the source file.
Input is larger than the 100 MB limit for this tool. For bigger files use the CLI: mlr --icsv --ojson cat data.csv- The page-side refusal: it fires before parsing begins and leaves the input pane untouched. Miller applies the same RFC 4180 reading to files of any size on your own machine, and the command shown converts CSV straight to JSON.
FAQ
Why does every value come out as a string until I enable Detect types?
Because coercion is lossy in exactly the places that hurt: leading zeros vanish from postal codes, and integers past 2^53 round silently. Cell text kept as strings cannot lie. With detection on, two guards keep those classes as strings anyway, and a note names each protected cell.
How does a 100 MB CSV file get converted without freezing the tab?
The file is parsed in chunks inside a worker: each batch of rows is serialized immediately and collected as parts of a Blob, a preview of the first rows appears quickly, and Download saves the assembled Blob. The tab stays responsive because neither input nor output ever has to be one giant string.
Why did an Excel export show é as é, and what does this page do about it?
That mojibake is Windows-1252 bytes read as UTF-8. Dropped files are sniffed byte by byte, and when the bytes cannot be valid UTF-8, a warning proposes re-decoding as Windows-1252 — one click, no guessing. Pasted text arrives already decoded, so the sniff applies to files.
Which JSON shapes can the converter produce?
Four: an array of row objects (the default), an array of arrays carrying the grid with the header row first when one was detected, an object keyed by a column you choose, and NDJSON with one compact object per line. All four come from the same parsed table, so switching shapes never re-reads the input.
Can the same page convert JSON back to CSV?
Yes — paste JSON and the direction badge flips; the s key or the badge itself overrides it. Arrays of objects flatten to dotted columns down to a configurable depth, heterogeneous objects widen to the union of their keys, array values follow the policy you pick (stringify, join or error), and the writer quotes per RFC 4180.
Is NDJSON a valid JSON document?
No, and that is its value: the file as a whole does not parse, but every line parses alone, so a consumer can process a stream record by record, and a corrupt line costs one record rather than the file. Choosing the NDJSON shape switches the download to .ndjson.
What happens to semicolon-separated files where the decimal comma appears?
Excel in many European locales writes semicolons between fields precisely because commas sit inside numbers like 1,5. The delimiter vote recognizes that layout, and comma-decimal cells always stay strings — 1,5 is not a JSON number spelling, so even Detect types leaves it alone rather than inventing 1.5 or 15.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Run the conversion again right now, whatever the input size.
- ⌘/Ctrl + S
- Download the output — the extension follows the shape: .json, .ndjson or .csv.
- ⌘/Ctrl + C
- Copy the entire output when no text is selected; selections copy normally.
- ⌘/Ctrl + Shift + C
- Copy the entire output even while text is selected.
- s
- Swap between CSV → JSON and JSON → CSV — the detector re-runs, and a single undo restores the previous state.
- t
- Move to the next theme.
- ? or ⌘/Ctrl + /
- Open the shortcut reference, browser conflicts noted.
- Esc
- Close the topmost dialog — the panes are never touched.