Skip to the tool
DevToolBench

CSV to JSON Converter

CSV into JSON with a parser that survives quotes and line breaks.

0 chars
Rows
0
Columns
0
Delimiter
Characters
0

Everything runs in your browser. Nothing you type is sent to a server.

Found a bug in this tool? Report it.

Share this tool

What the parser actually does

This CSV to JSON converter reads your rows with a full RFC 4180 parser in the browser and gives back either an array of objects keyed by the header row, or an array of arrays. Quoted commas survive, doubled quotes collapse to one, newlines inside a cell stay inside that cell, and \r\n, \n and a lone \r all end a record.

Nothing is uploaded. That matters more here than for most converters, because the file people paste into a CSV tool is usually an export of something real — a customer list, an order log, a payroll run.

Why split(',') is the wrong answer

It is the first thing everyone writes, and it is wrong for a reason that will not show up in your test fixture. In CSV, a comma inside double quotes is a character, not a boundary. So the moment a row contains "Ada, Countess", splitting produces two fields where there is one, every later column shifts, and the row lands in the wrong shape.

Two more rules break the same shortcut. A quote inside a quoted field is written twice, so "He said ""hi""" is a single value that contains one pair of quotes. And a quoted field may contain a line break, which destroys the assumption that one line is one record — a file where three addresses have a second line will parse as three extra rows, and the count will look plausible.

The correct approach is a character-by-character state machine with one bit of memory: are we inside quotes? It is about forty lines, it never guesses, and it is what runs on this page.

Types, and the fields that only look numeric

Converting "36" to 36 is usually what you want. Converting "007" to 7 never is.

The rule used here is strict on purpose: a value becomes a number only if it matches what the JSON specification itself calls a number. That excludes leading zeros, a leading plus, a trailing dot, a comma used as a decimal mark and anything with surrounding whitespace. The practical effect is that postcodes, phone numbers, account codes, ISBNs and SKUs stay as strings, which is the only correct outcome — they are identifiers that happen to be written with digits.

Booleans are recognised in any case, because spreadsheets export TRUE and FALSE in capitals. yes and no are left alone, since they are ordinary answers in a survey export as often as they are flags.

Empty cells are the one genuinely ambiguous case, so they get their own toggle. An empty string is honest about what the file contains; null is more useful when the JSON goes into a database that distinguishes missing from blank.

The header row, and what happens without it

With the header toggle on, the first record becomes the keys. Blank header cells are named column_2 and so on by position, and a repeated name gets a _2 suffix instead of quietly overwriting the earlier column — a duplicate id in a joined export is common and losing one of them is not acceptable.

With it off, every record is data and every key is generated. That is the right setting for a file that was already stripped of its header, and for the fragment you pasted out of the middle of a larger file.

Ragged rows are padded rather than rejected, and the extra values are kept under generated names. Seeing column_9 appear in your output is a signal worth chasing: it usually means an unescaped quote somewhere above, which has merged two records into one.

Going the other way, the JSON to CSV converter applies the same quoting rules in reverse and flattens nested objects into dotted columns. If what you actually want is a table to paste into a README or a pull request, the Markdown table generator takes the same pasted rows.

Frequently asked questions

Why can I not just split each line on a comma?

Because a comma inside a quoted field is data, not a separator, and splitting cannot tell the difference. The same applies to a doubled quote and to a newline inside a quoted cell, which breaks the earlier assumption that one line equals one record. A state machine that reads one character at a time and remembers whether it is inside quotes handles all three; a split handles none of them, and it fails on exactly the rows you care about, the ones with addresses and free text in them.

Why did my product code keep its leading zeros here?

Because the type conversion only accepts what JSON itself would accept as a number, and a leading zero disqualifies a value. So 007 stays the string 007, and so do a plus-prefixed phone number, a value with a trailing dot and anything with a thousands separator. This is deliberate. Postcodes, account references, ISBNs and SKUs are text spelled with digits, and turning them into integers is a data loss that nothing downstream can undo.

What happens if a row has more or fewer fields than the header?

Nothing is dropped and nothing throws. A short row gets the missing keys with an empty value, or null if you asked for that. A long row gets generated names such as column_7 for the surplus values, so a ragged export is visible in the output instead of silently truncated. Ragged rows almost always mean an unescaped quote further up the file, so it is worth finding the cause rather than accepting the padding.

How is the delimiter detected?

The first record is parsed once with each candidate — comma, semicolon, tab and pipe — and the one that produces the most fields wins, with the comma winning any tie. Because the detection uses the same quote-aware parser, a semicolon inside a quoted cell does not vote. If the guess is wrong, or the file has only one column, pick the delimiter by hand and the detection is skipped entirely.

What should I do about the encoding?

A byte order mark at the start of the file is removed here, so the first column is never named with an invisible character glued to it. Beyond that, paste is paste — your browser has already decoded the bytes. If accented characters look wrong before you paste, the file is not UTF-8, and the fix belongs in whatever exported it, usually by choosing CSV UTF-8 rather than plain CSV.

Related tools

Updated