Most JSON-to-CSV converters break the moment your data has nested objects or arrays. They either drop nested fields entirely, produce malformed CSV, or leave you writing custom parser code at 10pm. This guide shows you exactly how nested JSON to CSV conversion works, why standard tools fail, and how to handle it properly in JavaScript and Python — with working code you can copy today.
Why Nested JSON Breaks Standard Converters
CSV is a flat format. Each row is a single record. Each column is a top-level field. When your JSON has nested objects like {"user": {"name": "Alice", "city": "Berlin"}}, a naive converter has two choices: skip the nested data, or produce garbage output. Neither is acceptable.
The problem is that CSV has no native concept of hierarchy. You have to decide how to represent nested data as flat columns. The three common strategies:
- Dot notation —
user.namebecomes the column header - JSON string — nested objects are serialized as a JSON string inside a single cell
- Flatten with repeat — expand arrays into multiple rows (one row per array element)
Nominal's converter uses dot notation for objects and handles arrays with a configurable strategy. Let's walk through how it works.
Step-by-Step: Flattening Nested JSON in JavaScript
The core operation is a recursive flatten — traverse every level of nesting and build dot-notation keys. Here's a clean implementation:
function flattenNested(obj, prefix = '') { const result = {}; for (const [key, value] of Object.entries(obj)) { const newKey = prefix ? `${prefix}.${key}` : key; if (value !== null && typeof value === 'object' && !Array.isArray(value)) { const nested = flattenNested(value, newKey); Object.assign(result, nested); } else { result[newKey] = value; } } return result; }
Apply it to a real data structure:
const data = { orderId: 'ORD-8821', customer: { name: 'Alice Chen', tier: 'premium' }, items: [ { sku: 'WIDGET-A', qty: 3, price: 12.99 }, { sku: 'GADGET-B', qty: 1, price: 49.99 } ], shipping: { city: 'Berlin', country: 'DE' } }; console.log(flattenNested(data)); // { // orderId: 'ORD-8821', // 'customer.name': 'Alice Chen', // 'customer.tier': 'premium', // 'items': '[{"sku":"WIDGET-A","qty":3,"price":12.99},{"sku":"GADGET-B","qty":1,"price":49.99}]', // 'shipping.city': 'Berlin', // 'shipping.country': 'DE' // }
Converting the Result to CSV
With a flat object in hand, generating CSV is straightforward:
function toCSV(rows) { if (!rows.length) return ''; const headers = Object.keys(rows[0]); const escape = v => `"${String(v ?? '').replace('"', '""')}"`; const headerRow = headers.map(escape).join(','); const dataRows = rows.map(r => headers.map(h => escape(r[h])).join(',')); return [headerRow, ...dataRows].join('\n'); }
Python Alternative: Using Pandas
If you're working in Python, pandas.json_normalize handles nested flattening in one call:
import json import pandas as pd with open('data.json') as f: data = json.load(f) # Expand arrays into multiple rows (record_path) — one row per item df = pd.json_normalize( data, record_path='items', # array to expand meta=['orderId', ['customer', 'name']], # fields to carry over sep='.' ) df.to_csv('output.csv', index=False) # Output columns: # orderId | customer.name | sku | qty | price
For objects nested inside arrays, use the meta parameter to pull in parent-level fields at the row level. The sep='.' option gives you dot-notation column headers to match the JavaScript behavior above.
Common Edge Cases and How to Handle Them
- Null values: Represent as empty CSV cells — not the string "null"
- Booleans: Serialize as lowercase
true/false - Deeply nested objects: Path depth has no limit — dot notation handles any depth
- Mixed types: If a key is a string in one record and an object in another, coerce to string
- CSV with commas in values: Always quote fields that contain commas, quotes, or newlines
Handling Arrays Without Dropping Data
The most common mistake: serializing an array as its .toString() output, which gives you "item1,item2,item3" — useless for downstream processing. Instead, decide upfront:
- One row per element: Use the array expansion strategy (Python
record_path, or JavaScriptArray.flatMap) - One row per record with JSON string cell: Keep arrays as
JSON.stringify()cells — queryable but not human-readable - One row per record with joined string: Arrays as
"item1 | item2 | item3"— readable, but loses type info
Try It Live
If you'd rather not write the code yourself, Nominal's converter handles nested JSON to CSV automatically — client-side, no upload required, free for 2 conversions per day.
Nested JSON → CSV in seconds
No signup. No data sent to a server. Runs entirely in your browser.
When to Write Code vs. Use a Tool
If you need to convert nested JSON to CSV once, use a tool. If you need to do it regularly as part of a pipeline, write the code — the flatten function above is 30 lines and handles 95% of real-world cases.
The one scenario where code is strictly better: when you need to handle schema changes gracefully. A pipeline that rebuilds its column map from the actual keys in each input will never break on new fields, while a static converter that hardcodes column names will silently drop new data.
For occasional one-off conversions, a tool with dot-notation flattening is faster and less error-prone than writing and debugging a parser.
Summary
Nested JSON to CSV conversion requires two steps: flatten the nested structure into dot-notation keys, then emit those keys as CSV columns. The flattening step is where most tools fail — not the CSV generation.
For JavaScript, a recursive flattenNested function handles any depth of nesting. For Python, pandas.json_normalize does it in one call. For quick one-off conversions, Nominal's converter is already built and free to use.