CSV to JSON: converting without corrupting data

2026-09-02·Developer reference·3 min read·by Sourabh Singh

CSV to JSON: converting without corrupting data

Why CSV parsing is harder than splitting on commas, what nesting does to a conversion, and the type coercion that quietly destroys zip codes and IDs.

CSV to JSON: converting without corrupting data

CSV is flat and untyped, JSON is nested and typed, so conversion between them always loses something. Converting CSV to JSON forces a guess about types - which is how zip codes with leading zeros become integers - and converting JSON to CSV has to flatten or stringify anything nested.

Both formats look simple enough that people write their own converter. CSV in particular is where that goes wrong, because splitting on commas is correct for about 90% of real files.

CSV is not comma-separated

RFC 4180 defines a quoting mechanism, and any file containing free text will use it.

name,note
Ada,"Engineer, mathematician"
Grace,"She said ""hello"" first"
Alan,"Line one
line two"

Three things a naive split gets wrong here: the comma inside the quoted note, the doubled quotes representing a literal quote character, and the newline inside a quoted field, which means you cannot split the file into rows by line first.

A correct parser walks the file character by character tracking whether it is inside quotes. It is about thirty lines and worth using someone else's.

Free toolCSV to JSONTurn a CSV into an array of JSON objects in your browser, with proper handling of quoted fields, embedded commas and custom delimiters.

The delimiter is not always a comma

Locales that use a comma as the decimal separator - most of continental Europe - export CSV with semicolons, because otherwise 1,5 is ambiguous.

This is why a French or German spreadsheet export opens as a single column in an English tool. The fix is choosing the right delimiter, not repairing the file. Tab-separated files sidestep the problem entirely and are worth preferring for anything internal.

Encoding

CSV carries no encoding declaration. Excel on Windows historically wrote them in the system codepage rather than UTF-8, which is why accented characters and emoji arrive as mojibake.

If a file must open cleanly by double-clicking in Excel, write UTF-8 with a byte order mark. If it is going to a script, write plain UTF-8 and open it in Excel through Data then From Text/CSV, where you can set the encoding explicitly.

Type coercion is where data dies

CSV has no types. Everything is text. A converter producing JSON has to decide whether 007 is a string or the number 7.

Guess numerically and you destroy:

  • Zip codes: 01234 becomes 1234
  • Phone numbers with leading zeros or a + prefix
  • Product codes and account numbers with leading zeros
  • Long IDs above 2^53, which lose precision as JavaScript floats
  • Values like 1e5, NaN and Infinity that were meant literally

Guess textually and every numeric column needs converting downstream.

There is no correct default, which is why any conversion of data that matters should be checked against the source. If you control the export, quote the columns that must stay text.

Nesting

JSON expresses hierarchy; CSV cannot. Converting JSON to CSV means one of three things:

Stringify the nested value into a cell. Preserves everything, unreadable in a spreadsheet.

Flatten with dotted keys, so user.address.city becomes a column. Works well for objects, badly for arrays of varying length.

Explode into multiple rows, one per array element, repeating the parent fields. This is what a database join produces and is usually what an analyst actually wants.

Pick deliberately. Silently stringifying is the default in most tools and the reason exports look fine and are useless.

Missing keys

When converting an array of objects to CSV, the header row is the union of every key across every object. An object missing a key gets an empty cell.

An empty cell is ambiguous: it could mean absent, empty string or null. If that distinction matters in your data, CSV is the wrong interchange format and you should be sending JSON Lines instead - one JSON object per line, which keeps types and nesting while staying streamable.

In a script

Python: csv.DictReader and json.dump for one direction, pandas.read_json and to_csv for the other. Node: csv-parse and json2csv. Command line: jq -r with @csv handles the JSON-to-CSV direction well.

For a one-off, use the CSV to JSON converter or the JSON to CSV converter - both parse quoted fields properly and run in the browser, so a customer export never leaves your machine.

Tools from this guide