← All guides

Converting CSV to JSON: Where It Actually Goes Wrong

Splitting a CSV line on commas works right up until a field contains a comma. RFC 4180 solves this by letting a field be wrapped in double quotes, and a literal quote inside that field gets doubled:

name,note
Ada,"Works on Tuesdays, sometimes Thursdays"
Bob,"He said ""hi"" first"

A converter that just calls split(',') per line will cut the first row into three fields instead of two, and mangle the escaped quote in the second. Handling this correctly means walking the string character by character and tracking whether you're inside a quoted field — a simple state machine, not a split.

Every value comes back as a string, on purpose

A naive converter looks at a CSV cell like 501 and decides it must be a number. That guess is wrong for a US zip code — 00501 (Holtsville, NY) loses its leading zeros the moment it's coerced to a number, and now it's a five-character string turned into a three-digit integer. The safer default is to leave every field as the string it actually is in the CSV, and let you decide downstream whether "501" should become a number. Coercion guesses right about as often as it guesses wrong.

Header row rules

The first row is treated as column names. A header with an empty column name (two commas in a row, name,,email) is rejected outright rather than silently creating a field called "" — that's almost always a sign the header row itself is malformed, not a legitimate empty column name.

Going the other way: JSON to CSV expects flat objects

Converting back, the input has to be a JSON array of objects — not a single object, not an array of arrays. Any value that isn't a plain string, number, boolean, or null gets JSON.stringify'd into that one cell rather than flattened into separate columns:

[{ "id": 1, "tags": ["a", "b"] }]

↓

id,tags
1,"[""a"",""b""]"

That's a deliberate scope limit, not a bug — flattening nested structure into dotted column names (address.city, address.zip) is a real feature some CSV tools offer, but it makes assumptions about which nesting pattern you want that don't hold for every shape. If you need every record flattened consistently, check the output before assuming the tags column is unusable — it's valid JSON, just JSON living inside one CSV cell.

Try the CSV ↔ JSON Converter