How nested JSON maps to a flat CSV
CSV is a grid: one header row, then one row per record, with the same columns in every row. JSON is a tree. To bridge the two, every branch of the tree is turned into a single column whose name is the path to that leaf, joined with dots:
{ "name": "Alice", "address": { "city": "Berlin", "zip": "10115" } }
name , address.city , address.zip
Alice, Berlin , 10115
The flattening rules this tool uses:
- Objects are walked recursively.
a.b.cis the value at{"a":{"b":{"c": ...}}}. - Arrays are handled per the Inner arrays option. Join writes the elements into one cell separated by
;(objects in the array are written as compact JSON). Indexed columns emitstags[0],tags[1], and keeps flattening object elements intoitems[0].sku. - The header is the union of every leaf path seen in any row, in first-seen order. A key that appears in only some records still gets a column.
- A missing key is an empty cell, not the string
null. An explicit JSONnullis also written as an empty cell. - Scalars are written as-is: numbers unquoted,
true/falseliterally, strings quoted only when the content requires it.
The RFC 4180 quoting rules
RFC 4180 is the closest thing CSV has to a standard. A field must be wrapped in double quotes when it contains any of:
- the delimiter (a comma in a normal CSV),
- a double quote — which is then escaped by doubling it:
She said "hi"becomes"She said ""hi""", - a line break (CR or LF).
A field with none of those may be written bare. Quote every field mode wraps all of them regardless — slightly larger, but some brittle parsers are happier with it. Records are separated by CRLF in the spec; keep CRLF unless you know the consumer wants Unix line endings.
Arrays of arrays vs arrays of objects
| Input shape | Example | What you get |
|---|---|---|
| Array of objects | [{"a":1,"b":2},{"a":3,"b":4}] | The normal case. Keys become the header; one row per object. |
| Array of arrays | [["a","b"],[1,2],[3,4]] | Positional columns col1, col2, ...; each inner array is a row. If the first row looks like headers, you may want a CSV that keeps it as data — check the output. |
| Array of scalars | ["x","y","z"] | A single column named value, one value per row. |
| Single object | {"a":1,"b":2} | Treated as a one-row table. |
| A bare string, number, or boolean | 42 | Rejected with a message — there is no table to build. |
Excel gotchas
- UTF-8 without a BOM shows as mojibake. Excel on Windows assumes the system code page unless the file starts with a UTF-8 byte-order mark. If names with accents or non-Latin text look wrong, re-export with Add UTF-8 BOM on. Google Sheets and most programming languages want it off.
- Leading zeros disappear. A ZIP code
01234or a part number007is read as a number and the zeros are dropped. There is no fix inside the CSV; import via Data → From Text/CSV and mark the column as Text. - Things become dates.
3-4,1/2, andMAR-1get silently converted. Same fix: typed import, or open in Sheets which prompts first. - Long numbers lose precision. A 16+ digit ID becomes
1.23457E+17. Keep it as text on import.
Questions
How does a nested object become CSV columns?
Each nested path becomes one dot-named column: {"address":{"city":"Berlin"}} gives address.city. The header is the union of all leaf paths across every row.
What happens to arrays inside an object?
Your choice: join puts the array in one cell separated by ;, indexed columns makes roles[0], roles[1] and keeps flattening object elements.
What about objects missing keys others have?
The header is the union of all keys, so the column exists and rows without that key get an empty cell.
How do I stop Excel mangling ZIP codes and dates?
Import through Data → From Text/CSV and set those columns to Text, or open the file in Google Sheets. CSV itself cannot mark a field as text.
Is my JSON uploaded?
No. Parsing, conversion, and the download all happen in your browser.