Guide
How to convert JSON to CSV (including nested data)
A developer's guide to turning JSON into CSV — the expected shape, how keys become headers, and how to flatten nested objects and arrays — with a converter built in.
CSV is a flat grid: rows and columns, nothing more. JSON is a tree that can nest objects inside objects and arrays inside arrays. Converting JSON to CSV is the job of flattening that tree into a grid without losing data. The converter above does this for you; understanding the rules below tells you what shape of input works and where the edge cases are.
The shape CSV expects
CSV maps most naturally from a JSON array of objects, where each object is one row:
[
{ "id": 1, "name": "Ada", "active": true },
{ "id": 2, "name": "Linus", "active": false }
]
This converts directly to:
id,name,active
1,Ada,true
2,Linus,false
Each object key becomes a column header, taken from the top of the file once, and each object's
values fill one row. A single object (not wrapped in an array) produces one data row. A bare array
of values — [1, 2, 3] — has no keys, so there are no column names to derive; that is the one
shape that does not convert cleanly.
Handling inconsistent keys
Real-world JSON arrays are rarely uniform. Some objects have keys others lack:
[
{ "id": 1, "name": "Ada", "team": "core" },
{ "id": 2, "name": "Linus" }
]
A correct converter scans every object to build the full set of columns, then leaves a blank cell wherever a row is missing that key:
id,name,team
1,Ada,core
2,Linus,
This is why the header row reflects the union of all keys, not just the keys of the first object. If you rely only on the first row to define columns, later rows with extra fields silently lose data.
Flattening nested objects
CSV has no nesting, so a nested object is collapsed into multiple columns using a path-style header, typically joined with a dot:
[{ "id": 1, "address": { "city": "Oslo", "zip": "0150" } }]
becomes:
id,address.city,address.zip
1,Oslo,0150
Each leaf value in the tree gets its own column, named by the path from the root to that value.
Deeper nesting just produces longer paths such as address.geo.lat.
Flattening arrays
Arrays inside a record are the hardest case, because a list does not fit in one cell. Two common strategies:
- Join into one cell.
"tags": ["a", "b"]becomes a single columntagswith value"a;b"(or comma-joined and quoted). Compact, but you lose per-item columns. - Expand to indexed columns.
tags.0,tags.1, and so on — one column per position. Precise, but the column count depends on the longest array.
Which is right depends on whether you need to read individual items back out later. For tags or labels, joining is usually fine; for fixed-size tuples, indexed columns preserve more.
Escaping: commas, quotes, and newlines
A value that itself contains a comma, a double quote, or a newline would break the grid if written raw. CSV (RFC 4180) handles this by wrapping the value in double quotes and doubling any internal quote:
name,note
Ada,"Lovelace, Ada"
Linus,"He said ""hi"""
The converter applies this automatically, so values with punctuation stay intact. The main thing to watch on your side is the input: make sure your JSON is valid and that the records share a consistent structure. With a clean array of objects in, the converter above produces a well-formed, correctly escaped CSV you can open in any spreadsheet or load into a database.