Converting CSV and JSON without losing data

How headers, delimiters, quoting and nested objects behave when converting between CSV and JSON, and where data quietly gets lost.

Published 2026-09-25

Two formats built for different shapes

JSON naturally holds nested, variable-shaped data: objects inside objects, arrays of different lengths, optional fields. CSV is flat by design — a grid of rows and columns, defined loosely by RFC 4180. Converting between them is only mechanical when the data itself is already flat and uniform (a list of objects that all share the same simple fields). Everything else requires a decision about how to flatten or reconstruct structure, and that decision is where data quietly gets lost if you're not paying attention.

A simple, safe case:

[
  { "id": 1, "name": "Ada", "active": true },
  { "id": 2, "name": "Grace", "active": false }
]
id,name,active
1,Ada,true
2,Grace,false

This converts cleanly in both directions, as long as the reader knows 1 and true were a number and a boolean, because the CSV itself only stores text. Most real datasets are less forgiving.

Headers: where do column names come from

Converting JSON to CSV requires picking the column headers, and the only reliable source is the union of keys across every object in the array. If one row has a field others don't, a correct converter still creates that column and leaves other rows blank in it — silently using only the first row's keys as the header is a common bug that drops any field the first object happens not to have.

[
  { "id": 1, "name": "Ada", "team": "core" },
  { "id": 2, "name": "Grace" }
]
id,name,team
1,Ada,core
2,Grace,

Going the other direction, CSV to JSON, the first row is conventionally treated as the header row — RFC 4180 calls this an optional convention, not a hard requirement, so a CSV to JSON tool has to assume it (nearly universal in practice) or let you say otherwise. Every row becomes an object keyed by those header names, in row order.

Delimiters and quoting

CSV's name promises commas, but plenty of real files use semicolons (common in European locales where comma is the decimal separator), tabs (TSV), or pipes. A converter that hardcodes comma-splitting breaks on any of these — check what delimiter your source file actually uses before converting, and set it explicitly rather than trusting auto-detection on a small sample.

Quoting is where CSV gets genuinely tricky. Per RFC 4180 §2.5–2.7:

  • A field containing the delimiter, a double quote, or a line break must be wrapped in double quotes.
  • A literal double quote inside a quoted field is escaped by doubling it: " becomes "".
id,note
1,"Says ""hello"" to everyone"
2,"Multi-line
note here"

A converter that splits naively on commas instead of respecting quotes will shred both of these rows — the embedded comma or newline inside the quotes will be misread as a new field or a new row. This is the most common source of corrupted CSV-to-JSON conversions, and it's why hand-rolled line.split(",") code breaks on real-world exports the moment a name or note field contains a comma.

Numbers, booleans, and why "everything is a string" in CSV

CSV has no type system — every cell is just text. JSON distinguishes numbers, strings, booleans and null. That mismatch runs in both directions:

JSON to CSV is lossy for types: 42 (a number) and "42" (a string) both become the literal text 42 in the CSV cell, and nothing in the CSV file records which one it originally was. Converting back to JSON later, a converter has to guess — most will re-infer 42 as a number, silently changing a string field's type from the original data.

CSV to JSON faces the same ambiguity from the other side. A cell containing 007, 1.50, or 01234 looks numeric, but converting it to a JSON number strips the leading zero or trailing zero — turning a zip code "02139" into 2139, or a price "1.50" into 1.5. If the field is meaningfully a string (zip codes, phone numbers, IDs with leading zeros, version numbers), it has to stay a string through the conversion. Quoting the cell in the CSV does not do that on its own: quotes only delimit the field, and most parsers apply type inference to quoted and unquoted cells alike. Use a parser or setting that keeps every cell as text, or a per-column type map. A blanket "look numeric, convert to number" rule will corrupt exactly the fields where formatting matters. (This hub's CSV to JSON tool never infers types: every value comes out as a string, so 02139 stays "02139", but numbers and booleans come out as strings too.) Check numeric-looking columns after converting — this is the single most common silent data-loss bug in CSV/JSON pipelines.

Nested objects and arrays

JSON supports nesting; CSV doesn't. Converting a nested object to CSV forces a choice:

[{ "id": 1, "address": { "city": "Boston", "zip": "02139" } }]

A reasonable converter flattens this into dotted or bracketed column names:

id,address.city,address.zip
1,Boston,02139

Be aware that not every converter does that flattening. This hub's JSON to CSV tool expects flat objects and does not flatten: a nested object cell comes out as the literal text [object Object] and an array is written as a,b in a single quoted cell, so the nested data is lost without any error. Flatten nested JSON yourself first.

An array-valued field ("tags": ["a", "b"]) has no single correct CSV representation — common approaches are joining with a separator ("a;b", which breaks if a tag itself contains that separator) or emitting one column per possible index (which only works if array lengths are bounded and consistent). Either way, going from that CSV back to JSON does not automatically reconstruct the original nested shape — you get a flat object with a dotted key or a delimited string, not a nested object, unless the converter specifically knows to reverse that particular flattening convention. If your JSON has nested objects or arrays, decide on the flattening convention deliberately rather than trusting a converter's default and assuming it round-trips.

A practical checklist

  1. Confirm the delimiter your source file actually uses (comma, semicolon, tab) before converting.
  2. After JSON→CSV, check whether any objects had fields others lacked — those columns should show up with blank cells, not silently disappear.
  3. After CSV→JSON, check numeric-looking columns that are semantically strings (zip codes, phone numbers, SKUs with leading zeros) — make sure your parser keeps them as strings (quoting the cell in the CSV is not enough on its own).
  4. Run the JSON output through a JSON validator if it was hand-edited afterward — malformed quoting in the CSV step often produces JSON that looks right but has a shifted or merged field.
  5. For nested JSON, decide the flattening convention (dotted keys, joined arrays) up front rather than relying on a converter's default and assuming it's reversible, and look for [object Object] in the output, the sign that nested data was stringified instead of flattened.

Use the JSON to CSV converter when you need a spreadsheet-friendly export of an API response or a config array, and the CSV to JSON converter when a script needs structured data out of a spreadsheet export — but treat any round trip through both tools as an opportunity to check types and structure, not as a guaranteed lossless operation.

Tools mentioned in this guide