How to flatten nested json for clean csv export
- Step 1Save the JSON you want to export as a .json file — The flattener reads a dropped file, not pasted text. For a multi-record export, save the JSON array of objects to a file. Each top-level object should be one would-be CSV row — but note the flattener does not turn array elements into rows by itself (see step 5).
- Step 2Drop the file onto the dropzone — Flattening runs in your browser. The header shows the file name and size. Invalid JSON (trailing commas, single quotes) throws a
SyntaxErrorinstead of producing partial output — fix the source first if that happens. - Step 3Set the delimiter to underscore — Type
_into the Delimiter box (default is.). Underscore producesuser_address_city— valid as a bare column name in Excel, pandas, and most SQL dialects. Dot-delimited columns (user.address.city) work in pandas with bracket access but need quoting in SQL and confuse some spreadsheet importers. - Step 4Choose how arrays become columns —
dot.0.keymakestags_0,tags_1columns;arr[0].keymakestags[0](brackets usually need quoting); keep arrays leaves the array as a value so the CSV converter serialises it into a single cell. For a clean CSV, keep arrays is usually best — onetagscolumn with a JSON or comma-joined value beats a forest of positional columns. - Step 5Flatten one record, or flatten the whole array — A top-level array flattens to
0.name,1.name,2.name— numeric prefixes, one object — it is not split into rows. To produce a real CSV, flatten one representative object to design your column set, then pass the array to json-to-csv, which handles the row layout and uses the flat keys as headers. - Step 6Hand the flat JSON to the CSV converter — Copy or download the flattened JSON, then open json-to-csv. Every flattened key now maps to a column header with a scalar (or single-cell array) value — no
[object Object], no dropped fields. For array elements that should become rows, see the row-handling notes in that tool.
Delimiter choice and CSV column-name safety
The Delimiter box accepts up to 3 characters (default .). Choose based on where the CSV lands.
| Delimiter | Example column | Excel | pandas | SQL (bare identifier) |
|---|---|---|---|---|
_ (underscore) | user_address_city | Clean — no quoting | df['user_address_city'] or df.user_address_city | Valid bare identifier |
. (dot, default) | user.address.city | Imports, but dots can confuse formula refs | df['user.address.city'] only (no dot accessor) | Needs quoting ("user.address.city") |
__ (double underscore) | user__address__city | Clean | Clean | Valid; useful when single _ appears in source keys |
- (hyphen) | user-address-city | Imports as text | df['user-address-city'] only | Needs quoting — hyphen is the minus operator |
Array handling for CSV columns
Same array input across the three modes. For most CSV exports keep arrays gives the cleanest column layout.
| Mode | Input field | Resulting key(s) | CSV result |
|---|---|---|---|
| dot.0.key (default) | tags: ["a","b","c"] | tags_0, tags_1, tags_2 | Three columns — sparse if rows have different array lengths |
| arr[0].key | tags: ["a","b"] | tags[0], tags[1] | Bracketed column names usually need quoting in the CSV header |
| keep arrays | tags: ["a","b","c"] | tags (array kept) | One column; the CSV converter decides how to serialise the array into the cell |
| keep arrays | items: [{...},{...}] | items (array of objects kept) | One column holding a JSON string — flatten or split that array separately if you need item columns |
Cookbook
Before/after JSON headed for a CSV export. The recurring theme: flatten the object shape here, let the CSV converter handle rows.
Nested object becomes clean CSV columns
ExampleThe core use case. Underscore delimiter turns a two-level object into flat keys that become spreadsheet-safe column headers.
Input (record.json):
{
"user": { "name": "Sue", "address": { "city": "NYC" } },
"plan": "pro"
}
Delimiter: _ Array handling: keep arrays
Output:
{
"user_name": "Sue",
"user_address_city": "NYC",
"plan": "pro"
}
CSV header row: user_name,user_address_city,planTop-level array is NOT one row per element
ExampleA common misconception. The flattener treats a top-level array as one object with numeric-prefixed keys — it does not produce one CSV row per element. Use json-to-csv for the row layout.
Input:
[ { "name": "Sue" }, { "name": "Jon" } ]
Flattener output (one object, numeric prefixes):
{
"0.name": "Sue",
"1.name": "Jon"
}
For a real 2-row CSV, pass the original array to
json-to-csv instead — flatten a single object here only
to design the column set.Keep arrays so tags become one column
ExamplePrimitive arrays in dot mode explode into positional columns that are sparse when row array lengths differ. Keep arrays gives you one tidy column the CSV converter serialises.
Input:
{ "id": 1, "tags": ["sale", "new"] }
dot.0.key → id, tags_0, tags_1 (positional, sparse)
keep arrays → id, tags (one column)
With keep arrays, json-to-csv writes the tags cell as
a single serialised value rather than spreading it across
tags_0 / tags_1 columns that differ per row.Avoiding [object Object] in the final CSV
ExampleThis is the failure flattening exists to prevent. Without flattening, a naive converter writes [object Object] for the nested field; with flattening, each sub-field is its own scalar column.
Input:
{ "order": 7, "customer": { "first": "A", "last": "B" } }
WITHOUT flattening (naive converter):
order,customer
7,[object Object]
WITH flattening (delimiter _), then json-to-csv:
order,customer_first,customer_last
7,A,BUnderscore collision needs a different delimiter
ExampleIf source keys already contain underscores, an underscore delimiter can collide. Switch to a delimiter that does not occur in your keys to keep paths unambiguous.
Input:
{ "user_id": 1, "user": { "id": 2 } }
Delimiter: _ → both map to user_id, last wins:
{ "user_id": 2 } ← user_id:1 silently lost!
Delimiter: __ → paths stay distinct:
{ "user_id": 1, "user__id": 2 }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.
Top-level array flattens to numeric keys, not rows
By design[ {...}, {...} ] becomes one object with 0.field, 1.field keys — it is not split into one CSV row per element. This is a flattener, not a row-shaper. Flatten a single object to design columns, then use json-to-csv for the actual row layout.
Primitive arrays create sparse positional columns
By designIn dot/bracket mode, tags: ["a","b"] becomes tags_0, tags_1. When rows have different array lengths, the CSV ends up with sparse trailing columns (tags_5 populated in only a few rows). Use keep arrays to get one tags column the converter serialises consistently.
Underscore delimiter collides with underscores in source keys
Data lossIf a key is literally user_id and there is also a nested user → id, an underscore delimiter maps both to user_id and the later one overwrites the earlier (last write wins). Switch the Delimiter box to __ or another character that does not appear in your source keys.
Empty objects and arrays vanish — column missing
Expected{} and [] produce no key. If a field is empty in the record you flattened, its column will be absent from the flat key set — so it never reaches the CSV header. Flatten a record where every column you want is populated.
Bracket-mode column names need quoting
Spreadsheet riskarr[0].key mode yields tags[0] headers. Square brackets in a CSV header force quoting in some tools and break SQL COPY/LOAD of the resulting file. Prefer underscore delimiter with dot mode, or keep arrays.
Invalid JSON throws before any output
errorTrailing commas, single-quoted strings, or NDJSON (one object per line) cause JSON.parse to throw a SyntaxError and no CSV-prep output appears. Fix the JSON first with json-format-fixer, or json-validator to find the exact position.
Deeply nested record stops flattening at level 10
ExpectedMax depth is fixed at 10. A record nested deeper than that leaves the inner subtree as a JSON value under the depth-10 key, which the CSV converter then writes as one big cell. Re-flatten that fragment separately if you need its inner fields as columns.
Output is JSON, not CSV
ExpectedThis tool produces flattened JSON, not a CSV file. The download is a .flat.json. Convert it with json-to-csv to get an actual CSV. The two-step split keeps the flattening logic focused and lets you reuse the flat JSON for non-CSV targets too.
Free-tier 2 MB / one-file limit
Tier limitFree tier caps JSON input at 2 MB and one file. A large multi-record export may be rejected. Flatten a single representative record to design columns (which is all you need for CSV prep), or upgrade to Pro (100 MB, 10-file batches) for the full export.
Frequently asked questions
Why am I getting [object Object] in my CSV cells?
Your converter met a nested object and could not represent it in a flat cell. Flatten the JSON first: drop the file here, set the Delimiter to _, and flatten so each nested sub-field becomes its own scalar key. Then run the flat JSON through json-to-csv — every key becomes a clean column and the [object Object] cells disappear.
Does flattening a JSON array give me one CSV row per element?
No. A top-level array flattens to one object with numeric-prefixed keys (0.name, 1.name) — it does not produce rows. This tool shapes the object, not the rows. Flatten a single object to design your columns, then pass the array to json-to-csv, which lays out the rows.
What delimiter should I use for CSV column names?
Underscore (_). It produces user_address_city — valid as a bare column name in Excel, pandas, and SQL with no quoting. The default . works in pandas (with bracket access) but needs quoting in SQL and can confuse spreadsheet importers. If your source keys already contain underscores, use __ to avoid collisions.
What happens to arrays when exporting to CSV?
It depends on the array mode. dot.0.key makes positional columns (tags_0, tags_1) that go sparse when rows differ in length. keep arrays leaves the array as a value so json-to-csv serialises it into a single cell — usually the cleaner choice. For array elements that must become their own rows, handle that in the CSV converter, not here.
My JSON is 10 levels deep — should I flatten all of it?
For CSV, flatten only the levels with meaningful scalar values. Note that the flattener stops at depth 10 anyway, leaving deeper subtrees nested as a single cell. Deep nesting beyond 3–4 levels usually means a sub-entity that belongs in a separate CSV with a foreign key, not extra columns.
Can I paste JSON instead of uploading a file?
No — the tool reads a dropped .json file and has no paste box. Save your JSON to a file first. If you only have a string, paste it into a new file in your editor, save, and drop that file onto the dropzone.
Will the flattener change my numbers or dates?
No. The flattener only restructures keys — leaf values keep their JSON type exactly. Numbers stay numbers, booleans stay booleans, null stays null, empty strings stay empty strings. Date handling and quoting are decided by the CSV converter, not here.
Why did one of my columns disappear after flattening?
That field was probably an empty object {} or empty array [] in the record you flattened — those produce no key at all. Flatten a record where every field you need is populated, or merge several records so the union of fields is represented.
Is my customer data uploaded anywhere?
No. Flattening runs entirely in your browser. API responses, customer records, and any business data in the JSON never reach JAD Apps servers. Only an anonymous file-processed counter (no content) is stored for signed-in dashboard stats, and you can opt out.
What is the difference between this and just using json-to-csv directly?
json-to-csv handles row layout and basic shape, but deeply nested objects still need flattening first to avoid [object Object] and dropped fields. Flatten here to get clean column names with your chosen delimiter, then convert. The two-step approach also lets you reuse the flat JSON for pandas, SQL, or other targets.
How large a file can I flatten for free?
Free tier caps JSON input at 2 MB, one file at a time. That covers a representative record for column design. Pro raises it to 100 MB with 10-file batches; higher tiers go further. For full exports beyond 2 MB, upgrade or flatten a sample to design columns and process the bulk file via the API runner.
Can I automate flatten-then-CSV in a pipeline?
Yes. GET /api/v1/tools/json-flattener returns the option schema; pair the @jadapps/runner and POST to /api/v1/tools/json-flattener/run locally to flatten, then chain the json-to-csv runner endpoint. Data stays on your machine. A typical flow is: fetch JSON → flatten (underscore, keep arrays) → json-to-csv → load into the warehouse.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.