CSV to JSON Converter (and Back) — Quoted Cells, Embedded Newlines, Ragged Rows
Paste CSV and get an array of objects, or paste JSON and get text back that a spreadsheet will open. The delimiter, the header row and the ragged rows are all reported instead of guessed at, and every step is a loop in plain JavaScript, with no server in the middle — what you paste never leaves this device.
Output
How the CSV side is read
What the CSV side actually reads
The parser follows RFC 4180, which is the set of rules a spreadsheet uses:
- A quote only means “quoted” at the start of a cell.
"abc"defin mid-cell keeps its quotes as data instead of ending the field. - Two quotes in a row are one quote.
he said ""hi""comes back ashe said "hi". - Line breaks inside quotes stay inside the cell — that is the whole reason a CSV needs a parser instead of
split(",")andsplit("\n"). - CRLF and bare LF both count as one break, and a trailing newline ends the last record rather than adding an empty one.
- Blank lines at the very start are skipped (the note says how many), because with the header option on they would otherwise replace your column names.
Cells are kept as strings. 007 stays 007, 1e5 stays 1e5, N/A stays N/A — a converter that “helpfully” typed your columns is worse than one that lied, because the next program can’t tell a lost zip code from a real number.
Keys, ragged rows, and what gets dropped
With the header option on, row 1 becomes object keys and every later row becomes an object.
- A row with too few cells gets
nullfor the missing keys: JSON has no way to say “this column wasn’t there”, and skipping the key entirely would make every record a different shape. - A row with too many cells cannot be represented at all — the note names the row, the width, and shows the first characters of what fell off. Nothing is silently truncated, which is the entire point of the message.
- Duplicate header names collapse, because one JSON key holds one value. The note says so rather than letting you discover it in the output.
- An empty header cell still needs a key, so that column is named
column3after its position; the note lists which ones. - With the header option off, the output is an array of arrays, which is the only shape that preserves a table whose rows have different widths.
Going back to CSV
The reverse direction accepts the three shapes a JSON table can have:
- Array of objects → header from the union of keys, in first-seen order, with empty cells where a record lacks a key.
- Array of arrays → rows written straight out, no header (there are no keys to build one from).
- One object → two lines: the keys as a header, the values as a row.
Anything nested inside a cell is written as JSON text rather than flattened, since there is no agreed way to spread a sub-object over columns; a spreadsheet will show the braces, which is honest. Cells containing the delimiter, a quote, or a line break are quoted on the way out, and line endings are CRLF because that is what Excel expects.
Questions
Q: Why does my file come out with one giant row? The delimiter is wrong for that file — European spreadsheets export semicolon-separated CSV. Click the semicolon button; the counts update immediately. Tab-separated files are just as common when the source is a database client.
Q: My JSON has 30,000 objects. Parsing is capped at 200,000 characters and 5,000 rows, and the note states how much was left out. Split the file or use a real streaming tool for that size; a browser tab holding the whole string is not the right instrument.
Q: Will this fix my mojibake? No. Reading the file’s bytes and deciding their encoding is not part of the conversion — you are pasting text that already arrived as Unicode, so a mis-decoded file stays mis-decoded. Save it as UTF-8 in whatever produced it.
Q: Does my data get uploaded? Splitting, keying, quoting and JSON output are loops inside the script: the file is never offered to a server, and what you paste never leaves this device.