Why Convert JSON to CSV?
Every data workflow eventually ends in a spreadsheet. Analysts use Excel. Product managers use Google Sheets. Business stakeholders demand CSV exports from every dashboard. Meanwhile, every modern API emits JSON. Converting between the two is one of the most common data-engineering tasks — and one of the most subtly broken. This tool converts an array of JSON objects to a spec-compliant CSV in your browser, with correct escaping, header derivation, and optional flattening for nested data.
The reason the task keeps being subtly broken is that CSV is deceptively hard. RFC 4180 defines exactly how commas, quotes, and newlines inside fields must be escaped, and how the first row acts as headers. Real CSVs from real tools frequently violate the spec (Excel emits its own dialect, especially in European locales). This converter follows RFC 4180 strictly, produces output that Excel and Google Sheets both handle correctly, and gives you a UTF-8 BOM toggle for the one case where strict compliance still breaks Excel.
Flattening Nested JSON
CSV is inherently flat — one value per cell, no nesting. Real JSON is usually not flat. The two industry-standard approaches to flattening are dot-notation column names and stringified sub-objects. Dot-notation is what pandas json_normalize() produces — a nested field like {"address": {"city": "NYC"}} becomes a column named address.city. This makes each nested field independently filterable in a spreadsheet or SQL query, at the cost of exploding column counts on deep hierarchies.
Stringified sub-objects keep the CSV narrow — each row has the same columns as the top-level keys, but any object or array value becomes a JSON string in a single cell. This is what most log aggregators use. Downstream consumers must parse the cell as JSON to work with it, but the CSV stays legible when the nested structure varies row-to-row.
JSON to CSV in JavaScript / Node.js
// Minimal spec-compliant CSV writer
function jsonToCsv(rows) {
if (!rows.length) return '';
const cols = Array.from(new Set(rows.flatMap(Object.keys)));
const esc = v => {
if (v == null) return '';
const s = typeof v === 'object' ? JSON.stringify(v) : String(v);
return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s;
};
const header = cols.join(',');
const body = rows.map(r => cols.map(c => esc(r[c])).join(',')).join('\n');
return header + '\n' + body;
}
// With papaparse (recommended for production)
const Papa = require('papaparse');
const csv = Papa.unparse(records, { quotes: true, header: true });JSON to CSV in Python (pandas)
import pandas as pd
# Flat JSON — one line
pd.read_json('data.json').to_csv('out.csv', index=False)
# Deeply nested JSON — flatten first
data = json.load(open('data.json'))
df = pd.json_normalize(data, sep='.') # dot-notation columns
df.to_csv('out.csv', index=False, encoding='utf-8-sig') # sig = UTF-8 BOM for Excel
# Without pandas (stdlib only)
import csv, json
records = json.load(open('data.json'))
keys = sorted({k for r in records for k in r.keys()})
with open('out.csv', 'w', newline='', encoding='utf-8') as f:
w = csv.DictWriter(f, fieldnames=keys)
w.writeheader()
w.writerows(records)The Excel UTF-8 BOM Problem
Windows Excel has a legacy quirk: when opening a .csv file, it defaults to Windows-1252 encoding unless the file starts with a UTF-8 byte-order mark (BOM: 0xEF 0xBB 0xBF). Without the BOM, non-ASCII characters (accented Latin, Cyrillic, CJK, emoji) render as mojibake. With the BOM, Excel reads the file as UTF-8 correctly. Our converter offers a "UTF-8 BOM" toggle — enable it when the target is Windows Excel with international content, disable it when the target is a Unix pipeline or a CSV parser that treats the BOM as a literal three-byte header (which some do incorrectly).
Delimiter Choice — Comma vs Semicolon
Every European Excel locale (French, German, Spanish, Italian, Dutch) uses the semicolon ; as the CSV field delimiter, not the comma. The reason: those locales use the comma as the decimal separator (1,234 means one point two three four), which conflicts with using it as a field separator. Emitting a comma-delimited CSV to a French Excel user will produce a single-column output where the whole row lives in cell A1. Our converter has a delimiter option — use ; for European Excel or if unsure of the audience.
Common Pitfalls
1. Losing type information. Everything in a CSV is a string. true becomes "true", 42 becomes "42", null becomes empty. If the consumer needs to preserve types, use JSON Lines (one JSON object per line) instead of CSV.
2. Arrays inside objects. A field like "tags": ["admin", "dev"] gets stringified to "[\"admin\",\"dev\"]" — usable but ugly. Alternative: use pipe-separated in-cell (admin|dev) or flatten to multiple columns (tags.0, tags.1).
3. Header drift. When objects have different keys, the CSV needs the union of keys as headers. Missing keys produce empty cells. Do NOT emit different headers per row — every mainstream CSV parser will break on that.
Related JSON Tools
- JSON Formatter (Parent Tool)
- JSON to YAML Converter — for config file targets
- JSON to XML Converter — for SOAP APIs and legacy systems
- Format JSON Online — beautify before converting
- Validate JSON Online — check syntax before export