Why JSON resists flatness: trees versus grids
A CSV is a grid: rows and columns, every cell a single flat value. JSON is a tree: objects inside objects, lists inside objects, objects inside lists. Flattening is the translation between those two shapes, and it is the one part of JSON-to-CSV conversion where a converter has to make judgment calls on your behalf — because there is no single \"correct\" answer.
Take the simplest nested record: { \"id\": 1, \"customer\": { \"name\": \"Ana\", \"city\": \"Madrid\" } }. A grid has no way to hold a \"customer\" that contains two things. So the converter must choose: give customer its own column and stuff the object in as text, or explode the object into customer.name and customer.city columns. Every converter makes this choice with the same handful of rules — and once you know them, you can predict your CSV before you ever press convert.
The running example we will flatten
Everything below is demonstrated on one file — three orders with three levels of nesting, a plain-value list, an empty list, an empty object, a null, and a list of objects. We ran this exact file through this site's real conversion engine (the same JavaScript that runs when you press Convert), and every output shown is what the engine actually produced:
[
{ "orderId": "A-101", "customer": { "name": "Ana Ruiz", "contact": { "email": "ana@example.com", "city": "Madrid" } }, "items": ["book", "pen"], "tags": [], "meta": {} },
{ "orderId": "A-102", "customer": { "name": "Ken Sato", "contact": { "email": "ken@example.com", "city": "Osaka" } }, "items": ["lamp"], "tracking": [{ "code": "TX-1", "status": "shipped" }, { "code": "TX-2", "status": "pending" }] },
{ "orderId": "A-103", "customer": { "name": "Lina Noor", "contact": { "email": null, "city": "Berlin" } }, "items": ["mug", null, "plate"] }
]Rule 1: nested objects become dot-notation columns
The first and most important rule: every level of object nesting becomes a column name with the levels joined by dots. customer.contact.email is not three columns — it is one column whose name is the full path down the tree. Here is the engine's actual output with flattening on:
orderId,customer.name,customer.contact.email,customer.contact.city,items,tags,meta,tracking
A-101,Ana Ruiz,ana@example.com,Madrid,book; pen,,{},
A-102,Ken Sato,ken@example.com,Osaka,lamp,,,"[{""code"":""TX-1"",""status"":""shipped""},{""code"":""TX-2"",""status"":""pending""}]"
A-103,Lina Noor,,Berlin,mug; ; plate,,,Nesting depth has no practical limit in the rule itself — we also tested a four-level object ({ a: { b: { c: { d: \"deep\" } } } }) and the engine produced a single column named a.b.c.d with the value deep. The dotted names sort and filter normally in a spreadsheet; you can rename the headers afterward in one pass if customer.contact.city is too long for comfort.
Rule 2: lists of plain values join into one cell
When a list contains only plain values — strings, numbers, booleans, nulls — the whole list collapses into a single cell with the values joined by \"; \" (semicolon-space). Look at the items column above: [\"book\", \"pen\"] became the single cell book; pen, and [\"mug\", null, \"plate\"] became mug; ; plate — the null turned into an empty segment, not a skipped one.
Two practical consequences. First, if you later pick a semicolon as your CSV delimiter, that joined cell gets quoted automatically (\"book; pen\") so the separator inside the cell cannot split it — the engine applies CSV quoting rules per cell, verified in the test above. Second, joining loses the boundary information: after flattening, book; pen is text, not a list. If you need to split items back into rows, see Rule 3.
Rule 3: lists of objects collapse into one JSON-text cell
This is the rule that surprises people. When a list contains objects — like the tracking array with its { code, status } entries — it cannot become columns (objects have structure, not just values) and it cannot join like plain values. So the engine stores the whole list as a quoted JSON-text cell. Above, the tracking column for A-102 holds the full JSON array as text, escaped per CSV rules.
Is that a limitation? Honestly: yes, for this shape. No row-expansion toggle exists in this converter — it never creates one CSV row per list item. If you actually need one row per tracking event, restructure before converting: pull the inner array out (convert [{ \"code\": \"TX-1\", \"status\": \"shipped\" }, ...] on its own) and you get clean code/status rows. One column of JSON text is the honest alternative to silently inventing rows you did not ask for.
Rule 4: empties and nulls follow exact rules
Nulls and empties are where most \"why is my CSV weird\" confusion starts, so here are the verified behaviors, no guessing:
- A null value becomes an empty cell. The email: null for Lina Noor produced an empty cell under customer.contact.email — no \"null\" text, no quotes.
- An empty list becomes an empty cell. The tags: [] for A-101 left the cell blank. (Note: this is the flatten-ON behavior; with flattening off the same list appears as the literal text [].)
- An empty object becomes the text {}. The meta: {} for A-101 produced a cell containing {} — the engine has nothing to expand, so it writes the object's literal shape.
- A null inside a list becomes an empty joined segment — mug; ; plate above — rather than being skipped or written as \"null\".
These are design decisions, not universal law, but they are consistent: null means \"nothing here\", and the CSV says nothing.
Flattening ON versus OFF, byte for byte
Same file, flattening off. Every top-level key keeps its own column and nested structures are stored as quoted JSON text:
orderId,customer,items,tags,meta,tracking
A-101,"{""name"":""Ana Ruiz"",""contact"":{""email"":""ana@example.com"",""city"":""Madrid""}}","[""book"",""pen""]",[],{},
A-102,"{""name"":""Ken Sato"",""contact"":{""email"":""ken@example.com"",""city"":""Osaka""}}","[""lamp""]",,,"[{""code"":""TX-1"",""status"":""shipped""},{""code"":""TX-2"",""status"":""pending""}]"
A-103,"{""name"":""Lina Noor"",""contact"":{""email"":null,""city"":""Berlin""}}","[""mug"",null,""plate""]",,,Notice the off-mode details: the columns shrink to the top-level keys (customer, items, tags, meta, tracking), each nested value is stringified raw JSON with CSV quote-doubling, and the empty list and empty object appear as literal [] and {} text. Off is the honest-but-ugly representation — correct when you need to preserve the exact nested structure (for example, before feeding rows back into a system that expects JSON-shaped fields). For analysis — filters, pivot tables, charts — flattening on is the right call, which is why it is the default.
Where the rows come from: arrays, API wrappers, and JSON Lines
Flattening decides the columns; something else decides the rows. The engine looks at your top-level value and follows three cases:
Case one: your top level is an array of objects — each object becomes one row. This is the orders example above: three objects, three rows. Case two: your top level is a single object — you get one row. Case three is the useful one: your top level is an object that wraps an array, the way API responses do — { \"data\": [ ... ], \"total\": 2 }. The engine detects the inner array (it checks common wrapper keys like data, results, users, items) and makes each element a row, dropping the wrapper fields. We verified this: { \"data\": [{ \"a\": 1, \"b\": 2 }, { \"a\": 3, \"b\": 4 }], \"total\": 2 } produced exactly the two data rows with columns a and b — total gone.
One more input shape worth knowing: the engine also accepts JSON Lines (NDJSON) — one JSON object per line. If your export is ten thousand lines instead of one big array, paste it as-is; it converts the same way.
Ragged data: missing keys just leave empty cells
Real exports are ragged: order A-102 has tracking but no meta, A-101 has meta but no tracking, A-103 has neither. The engine builds the column list as the union of every key found in every row, in order — so the header row above has all eight columns — and any row missing a key simply gets an empty cell under it.
This is why the headers come out in insertion order (orderId first, then the first-seen nested keys) rather than alphabetical order: the columns follow the shape of your data as the engine walks it. If a later row introduces a brand-new nested path, the column appears anyway — nothing is silently dropped.
Three edge cases that bite
First: keys that already contain dots. If your JSON has both a literal key \"user.name\" and a nested user: { name: ... }, both want the same column. We tested it: the nested value wins and the literal key's value is lost ([{ \"user.name\": \"x\", user: { name: \"y\" } }] produced one column user.name containing y). Rename dotted keys before converting if both values matter.
Second: mixed-type lists. A list like [1, \"a\", { \"b\": 2 }] is not \"all primitives\", so the engine treats it like an object list and stringifies the whole thing into one JSON-text cell — even though most of it was plain. Keep lists homogeneous if you want the \"; \"-join behavior.
Third: deep nesting is fine, but watch the column names. Ten levels of nesting gives you a ten-part dotted column name, which is technically correct and practically unreadable. Flatten everything, then rename the three columns you actually care about.
The 30-second flattening checklist
If you remember nothing else: leave flattening on for analysis, turn it off only to preserve structure; expect dot-notation columns and \"; \"-joined lists; pull object-lists out and convert them separately when you need one row per item; and treat empty cells as the honest representation of nulls — not as errors. The toggle is one click, but now you know exactly what that click does.
Related guides
- Can You Convert JSON to CSV? Yes — What to Know Before You Do (2026 Guide)
- How Can I Convert a JSON File to a CSV File Online? (2026 Guide)
- How to Turn JSON into CSV in Under a Minute: The Fastest Way for Every Situation (2026 Guide)
- How to Make CSV from JSON: 7 Decisions That Turn Raw Data into a Working File (2026 Guide)
- How Do I Convert JSON to CSV in VS Code? 4 Ways, From Click to One Keypress (2026 Guide)
- How to Flatten the JSON Data: Explode, Join, or Leave It Nested (2026 Guide)
- Is There a Way to Convert JSON to CSV? Yes — 5 Ways, Pick by Your Situation (2026 Guide)
- How to JSON to CSV Online: Do It in This Tab in 30 Seconds (2026 Guide)
- JSON to CSV Converter Download: What You Should Actually Download (2026 Guide)
- Convert Large JSON to CSV Online Free: How Big Is Too Big? (2026 Guide)
- What Is a JSON and CSV File? A Beginner's Guide to Both Formats (2026 Guide)
- What Does Flattening a JSON Mean? The Term Explained (2026 Guide)
- What Is JSON to CSV Online? The 3 Words Explained (2026 Guide)
- What Is JSON to CSV Converter Online? Anatomy of the Tool (2026 Guide)
- Convert JSON to CSV Locally: Keep the File on Your Machine (2026 Guide)
- What Are Ways to Convert JSON to CSV Locally? 6 Methods, Compared (2026 Guide)
- Why Your CSV Opens in One Column in Excel, and How to Fix It (2026 Guide)
- Why Your CSV Shows Weird Characters in Excel, and How to Fix the Encoding (2026 Guide)
- Why Excel Drops the Leading Zeros From Your CSV Codes, and How to Keep Them (2026 Guide)
- Why Excel Changes Your CSV Dates, and How to Keep Them (2026 Guide)
- Why Saving Your CSV in Excel Changes It, and How to Keep the Original Safe (2026 Guide)
- Why Excel Won't Open Your Big CSV File, and What to Do Instead (2026 Guide)
Frequently asked questions
What does flattening JSON to CSV mean?
Flattening is the step that turns nested JSON into a flat spreadsheet grid: nested objects become dot-notation columns (customer.contact.city), lists of plain values join into one cell with "; ", and lists of objects collapse into a single JSON-text cell. Without flattening, nested values are stored as raw quoted JSON text in one column each.
How do I flatten nested JSON objects into CSV columns?
Use a converter with a flattening option and leave it on — nested objects are expanded into dot-notation columns automatically, at any depth (customer.contact.email, or even a.b.c.d). On this site's converter, flattening is the default: paste or upload your JSON, convert, and each nested path becomes its own sortable, filterable column.
What happens to arrays when flattening JSON to CSV?
It depends on what the array holds. Lists of plain values (strings, numbers) join into a single cell with "; " — [\"book\", \"pen\"] becomes book; pen. Lists of objects are stored as one quoted JSON-text cell, because objects cannot become flat values. Empty lists become empty cells. Mixed lists are stringified whole, so keep lists homogeneous for predictable results.
Why does my flattened CSV have so many empty cells?
Empty cells are normal and honest: a null value becomes an empty cell, an empty list becomes an empty cell, and when rows have different keys (ragged data) the missing keys leave blanks. Check whether the empties line up with nulls in your source JSON — if they do, the conversion is correct.
Should I turn flattening on or off when converting JSON to CSV?
Leave it on for analysis — dot-notation columns are what pivot tables, filters, and charts need. Turn it off only when you must preserve the exact nested structure, e.g. before feeding rows back into a system that expects JSON-shaped fields. Off mode stores each nested value as quoted raw JSON text, which is correct but unusable for spreadsheets.
Can I get one CSV row per item in a nested array of objects?
This converter does not expand object-lists into rows — it stores them as one JSON-text cell. The reliable workaround: extract the inner array and convert it on its own (e.g. convert just [{\"code\":\"TX-1\"},{\"code\":\"TX-2\"}]) to get clean one-row-per-item output. Do the extraction before converting, not after.
Is there a free tool that flattens JSON to CSV online?
Yes — this site's converter is free, needs no sign-up, and flattens nested JSON by default (dot-notation columns, \"; \"-joined lists) entirely in your browser, so your data never leaves your device. Upload a file or paste JSON, leave flattening on, pick your delimiter, and download the CSV.
Have nested JSON? Flatten it now — nothing to install
Paste your nested JSON or upload the file: objects become dot-notation columns, lists join cleanly, and the conversion runs entirely in your browser. Free, no sign-up.
Open the JSON to CSV converter