How to flatten nested json to csv with dot-notation columns
- Step 1Drop your nested JSON file — Drag a
.json,.ndjson,.jsonl, or.txtfile onto the dropzone. Whether the file is one object, an array of objects, or line-delimited, the converter handles it. Free files cap at 2 MB. - Step 2Leave Flatten nested objects on — This is the default and the whole point of the tool: it recurses through every nested object and emits dot-notation columns. Toggle it off only when you want nested objects kept verbatim as JSON-string cells.
- Step 3Decide how arrays should serialise — Set Array values to JSON literal (re-parseable, the default), Pipe joined (
a|b), or Comma joined (a, b). Remember arrays never become extra rows or columns — they always land in one cell. - Step 4Pick a delimiter if needed — Comma is the default. Choose Semicolon for EU-locale Excel or Tab for pasting directly into Google Sheets.
- Step 5Convert and inspect the column count — Click Convert to CSV. The Columns stat tells you how many leaf paths were found — a high number signals deep nesting. Scan the first-10-rows preview to confirm the dot-notation headers match what you expect.
- Step 6Download the CSV — Use Excel-Ready CSV (UTF-8 BOM + CRLF) for spreadsheets, or plain Download CSV (LF) for code. The dot-notation headers are preserved exactly in both.
Flattening rules by value type
How each kind of JSON value is rendered when Flatten nested objects is on (the default).
| Value type | Input | Resulting column(s) / cell |
|---|---|---|
| Nested object | { "a": { "b": 1 } } | Column a.b with value 1 — recurses to any depth |
| Array of primitives | { "tags": ["x", "y"] } | Single column tags, one cell, serialised per the Array values setting |
| Array of objects | { "items": [ {"id":1} ] } | Single column items, one cell containing the JSON of the array — not expanded |
| null | { "x": null } | Column x with an empty cell |
| Empty object | { "meta": {} } | No meta column is emitted (nothing to flatten) |
Flatten on vs off
The same nested record converted with flattening enabled and disabled.
| Setting | Headers produced | Cell content |
|---|---|---|
| Flatten ON (default) | user.name, user.address.city | Each leaf in its own column |
| Flatten OFF | user | One cell holding {"name":"Ada","address":{"city":"NYC"}} |
Cookbook
Worked examples showing exactly how nesting maps to dot-notation columns.
Two-level nesting to dot-notation
ExampleA profile object nested inside a user object produces a flat column for every leaf, joined by dots.
Input:
[ { "id": 1, "user": { "profile": { "name": "Ada", "age": 36 } } } ]
Output CSV:
id,user.profile.name,user.profile.age
1,Ada,36Mixed depth across records
ExampleUnion-of-keys headers ensure a deeper leaf present in only one record still gets a column, with empty cells in the shallower record.
Input:
[
{ "id": 1, "meta": { "tier": "pro", "flags": { "beta": true } } },
{ "id": 2, "meta": { "tier": "free" } }
]
Output CSV:
id,meta.tier,meta.flags.beta
1,pro,true
2,free,Array of primitives joined for readability
ExampleComma-joined arrays read naturally in a spreadsheet cell instead of staying as a JSON literal.
Input:
[ { "id": 1, "roles": ["admin", "editor"] } ]
Array values = Comma joined:
id,roles
1,"admin, editor"Array of objects kept as one cell
ExampleArrays of objects are not exploded into rows. The whole array is serialised as a JSON literal in a single cell — extract the array path separately if you need a row per item.
Input:
[ { "id": 1, "items": [ {"sku":"A"}, {"sku":"B"} ] } ]
Output CSV:
id,items
1,"[{""sku"":""A""},{""sku"":""B""}]"Keeping the original object intact
ExampleTurn flattening off when a column needs to carry the nested object verbatim, for example to round-trip back into a system that expects JSON.
Input:
[ { "id": 1, "config": { "theme": "dark", "lang": "en" } } ]
Flatten OFF:
id,config
1,"{""theme"":""dark"",""lang"":""en""}"Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Very deep nesting produces hundreds of columns
ExpectedThere is no depth cap — a deeply recursive object yields one column per leaf, which can be hundreds wide. That is intentional. If you only need certain branches, prune the JSON first with json-key-filter before converting.
A key already contains a literal dot
By designIf an object key is itself "a.b", the resulting column header will look identical to a nested path a → b. The flattener does not escape dots in keys, so two different structures can collapse to the same header — rename such keys with json-key-renamer first if it matters.
Array of objects you wanted as rows
Not expandedArrays never fan out into rows. An items: [ {...}, {...} ] array stays in one cell. To get one row per object, extract the array path with json-path-extractor and convert that array on its own.
Empty nested object
By designAn empty object {} flattens to nothing — no column is created for it. If a record contains only empty objects it will contribute no columns to the header union.
Mixed types under the same path across records
SupportedIf value is an object in one record and a string in another, you get a flattened value.x column from the object records and a value column from the string records — both appear, with empties where the other shape applied. Inspect the preview to confirm the layout.
null vs absent key
By designBoth null and a missing key render as an empty cell; they are indistinguishable in the output. Use json-null-stripper beforehand if nulls need to be removed or treated specially.
File exceeds the free 2 MB limit
BlockedFree conversions cap at 2 MB / 500 rows. Larger nested datasets need Pro (100 MB / 100,000 rows) or splitting the file.
Invalid JSON
Invalid JSONA syntax error halts conversion with an Invalid JSON message pointing at the failure. Validate or repair the structure first with json-validator or json-format-fixer.
Frequently asked questions
How deep can the flattener go?
There is no configurable depth limit. It recurses through nested objects until it reaches a primitive, null, or array, emitting one dot-notation column per leaf.
Why aren't my arrays becoming separate rows?
By design. Arrays — of primitives or objects — are serialised into a single cell because they vary in length between records. CSV cannot represent variable-length arrays as fixed columns, so one cell is the safe mapping.
How do I get one row per item in an array of objects?
Extract that array path first with json-path-extractor so the array becomes the top-level list, then convert it. Each object then maps to a row.
What delimiter joins the path segments?
A literal dot (.). So { "a": { "b": 1 } } becomes the column a.b. Dots are not escaped, so keys that already contain dots can collide with nested paths.
What if a field exists in only some records?
Headers are the union of all leaf paths across every record, so the field still gets a column. Records lacking it get an empty cell, keeping the grid aligned.
Can I keep a nested object as-is in one column?
Yes — turn off Flatten nested objects. Each nested object is then written as a JSON string in a single cell instead of being expanded into dot-notation columns.
How are null values flattened?
A null leaf produces an empty cell, identical to an absent key. To treat nulls differently, pre-process with json-null-stripper.
Does it handle NDJSON files?
Yes. Auto detect falls back to NDJSON when whole-document JSON fails, and you can force NDJSON / JSONL for line-delimited input.
Will the dot-notation headers survive opening in Excel?
Yes. Header names are plain text and are preserved exactly. Use the Excel-Ready CSV download (BOM + CRLF) so encoding and line endings are correct.
Is the conversion done on a server?
No. All parsing and flattening run in your browser; the file never uploads to JAD Apps.
How wide a CSV can it produce?
There is no fixed column cap — width is driven by your data's leaf count. Very deep records can produce hundreds of columns; the Columns stat reports the total after conversion.
I only need a few branches of a big object. What should I do?
Prune first with json-key-filter to keep only the keys you want, then flatten the smaller object to a tidy CSV.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.