JSON to CSV: Complete Guide to Converting JSON Arrays to Spreadsheets
Learn how to convert JSON to CSV in Python, JavaScript, and online tools. Covers nested JSON flattening, arrays of objects, and handling edge cases.
Have broken JSON right now? Fix it free in under 1 second — no signup.
Fix My JSON →JSON is the universal language of APIs. CSV is the universal language of spreadsheets. At some point every developer needs to move data from one to the other — export API results to Excel, feed model output into a data pipeline, or share structured data with a non-technical colleague who works in Google Sheets.
This guide covers every angle of JSON-to-CSV conversion: the ideal data shapes, Python and JavaScript implementations, flattening strategies for nested structures, and the edge cases that break naive converters.
When JSON Maps Cleanly to CSV
Not all JSON converts to CSV naturally. The structure that maps perfectly is an array of flat objects where every object has the same keys:
[
{ "id": 1, "name": "Alice", "email": "[email protected]", "score": 95 },
{ "id": 2, "name": "Bob", "email": "[email protected]", "score": 88 },
{ "id": 3, "name": "Carol", "email": "[email protected]", "score": 72 }
]
This produces a clean CSV with a header row derived from the keys and one data row per object:
id,name,email,score
1,Alice,[email protected],95
2,Bob,[email protected],88
3,Carol,[email protected],72
If your JSON looks like this, conversion is trivial. If it doesn't, you need a flattening strategy before you convert.
Handling Nested JSON: Flattening Strategies
Real-world API responses are rarely flat. Here are the common nested shapes and how to handle each:
Nested Objects
[
{
"id": 1,
"name": "Alice",
"address": {
"city": "London",
"country": "UK"
}
}
]
Strategy: dot-notation key flattening. Collapse nested keys with a separator:
id,name,address.city,address.country
1,Alice,London,UK
Arrays of Primitive Values
[
{ "id": 1, "name": "Alice", "tags": ["admin", "user"] }
]
Strategy: join with a delimiter. The array becomes a single cell value:
id,name,tags
1,Alice,admin|user
Arrays of Objects (One-to-Many)
[
{
"id": 1,
"name": "Alice",
"orders": [
{ "order_id": 101, "total": 49.99 },
{ "order_id": 102, "total": 129.00 }
]
}
]
This is genuinely relational data. Options:
- Explode: Produce one row per order, repeating the parent fields (
id,name) — row count multiplies - Aggregate: Summarise the array per row (
order_count,total_spent) - Stringify: Serialise the array back to JSON as a single cell value (lossy for spreadsheet use)
Choose based on what downstream consumers need.
Python: json + csv Modules
The standard library covers flat arrays with no dependencies:
import csv
import json
import sys
def json_to_csv(json_path: str, csv_path: str) -> None:
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
if not isinstance(data, list) or not data:
raise ValueError("Expected a non-empty JSON array")
# Collect all unique keys across all objects (handles sparse objects)
fieldnames = list(dict.fromkeys(key for row in data for key in row))
with open(csv_path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
writer.writeheader()
writer.writerows(data)
print(f"Wrote {len(data)} rows to {csv_path}")
json_to_csv("data.json", "output.csv")
Python: Flatten Nested JSON with a Recursive Function
import csv
import json
def flatten(obj: dict, prefix: str = "", sep: str = ".") -> dict:
"""Recursively flatten a nested dict. Arrays of primitives are joined with |."""
result = {}
for key, value in obj.items():
full_key = f"{prefix}{sep}{key}" if prefix else key
if isinstance(value, dict):
result.update(flatten(value, full_key, sep))
elif isinstance(value, list):
if value and isinstance(value[0], dict):
# Array of objects: stringify as JSON (or explode — your choice)
result[full_key] = json.dumps(value)
else:
# Array of primitives: join
result[full_key] = "|".join(str(v) for v in value)
else:
result[full_key] = value
return result
def nested_json_to_csv(json_path: str, csv_path: str) -> None:
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
flattened = [flatten(row) for row in data]
fieldnames = list(dict.fromkeys(key for row in flattened for key in row))
with open(csv_path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore", restval="")
writer.writeheader()
writer.writerows(flattened)
nested_json_to_csv("nested.json", "output.csv")
Python: pandas for Data Analysis Pipelines
If you're working in a data analysis context, pandas is the most ergonomic option:
import pandas as pd
import json
Flat array — one line
df = pd.read_json("data.json")
df.to_csv("output.csv", index=False)
Nested — use json_normalize
with open("nested.json") as f:
data = json.load(f)
Flatten one level of nesting
df = pd.json_normalize(data, sep=".")
df.to_csv("output.csv", index=False)
Flatten with a nested array — explode the orders sub-array
df = pd.json_normalize(
data,
record_path="orders", # the key containing the nested array
meta=["id", "name"], # parent fields to include in each row
meta_prefix="parent_",
sep="."
)
df.to_csv("output.csv", index=False)
pd.json_normalize handles dot-notation flattening automatically and exposes fine-grained control over how nested arrays are handled via record_path and meta.
JavaScript / Node.js: json2csv Library
For Node.js pipelines, the json2csv library provides a clean API with good handling of nested objects:
import { Parser } from "json2csv";
import { readFileSync, writeFileSync } from "fs";
const data = JSON.parse(readFileSync("data.json", "utf-8"));
// Flat conversion
const parser = new Parser();
const csv = parser.parse(data);
writeFileSync("output.csv", csv, "utf-8");
// Flatten nested with dot-notation keys
const flatParser = new Parser({
flatten: true,
flattenSeparator: ".",
});
const flatCsv = flatParser.parse(data);
writeFileSync("output_flat.csv", flatCsv, "utf-8");
Install: npm install json2csv
Streaming Large Files in Node.js
For large JSON files that don't fit in memory:
import { createReadStream, createWriteStream } from "fs";
import { AsyncParser } from "json2csv";
import { pipeline } from "stream/promises";
async function convertLargeFile(inputPath, outputPath) {
const input = createReadStream(inputPath);
const output = createWriteStream(outputPath);
const parser = new AsyncParser({ flatten: true });
await pipeline(input, parser, output);
console.log("Done");
}
convertLargeFile("large.json", "output.csv");
Vanilla JavaScript (Browser or Node.js, no dependencies)
function jsonToCsv(data) {
if (!Array.isArray(data) || data.length === 0) {
throw new Error("Expected a non-empty array");
}
// Collect all keys
const headers = [...new Set(data.flatMap(Object.keys))];
const escape = (value) => {
if (value === null || value === undefined) return "";
const str = String(value);
// Wrap in quotes if value contains comma, quote, or newline
if (/[",\n\r]/.test(str)) {
return "${str.replace(/"/g, '""')}";
}
return str;
};
const rows = data.map((row) =>
headers.map((h) => escape(row[h])).join(",")
);
return [headers.join(","), ...rows].join("\n");
}
// Usage
const csv = jsonToCsv(data);
const blob = new Blob([csv], { type: "text/csv" });
const url = URL.createObjectURL(blob);
// Attach to an <a download> element to trigger browser download
Before Converting: Validate and Format Your JSON
If your JSON source is an API response, AI output, or exported file, there's a good chance it has minor issues — trailing commas, encoding problems, or non-standard values — that will cause JSON.parse() to fail before you ever get to the CSV step.
The recommended workflow:
- Paste your JSON into the JSON Formatter to pretty-print it and make the structure human-readable
- If there are parse errors, use AI JSONMedic to repair the JSON automatically
- Inspect the repaired, formatted JSON to plan your flattening strategy
- Then run the conversion
This saves the frustrating cycle of writing conversion code, running it against broken input, getting a cryptic error, and not knowing whether the problem is in your code or the source data. Fix the JSON first, then convert.
Handling Special Characters
CSV has its own escaping rules that differ from JSON's:
| Character | JSON | CSV (RFC 4180) |
|---|---|---|
Double quote " | \" | "" (doubled) |
Comma , | unescaped in string | wrap entire value in "..." |
| Newline | \n | wrap entire value in "..." |
Backslash \ | \\ | unescaped |
Most CSV libraries handle this automatically. If you're writing your own, always wrap values in double quotes when they contain commas, quotes, or newlines — and double any " characters inside the value.
Watch for these edge cases in JSON data that will trip up CSV conversion:
[
{ "note": "He said, \"hello\"" },
{ "note": "Line one\nLine two" },
{ "note": "100% complete" }
]
The first two require quoting in CSV; the third does not.
Reverse: CSV to JSON
The reverse conversion is straightforward — parse CSV rows into objects using the header row as keys:
import csv
import json
def csv_to_json(csv_path: str, json_path: str) -> None:
with open(csv_path, "r", encoding="utf-8", newline="") as f:
reader = csv.DictReader(f)
rows = list(reader)
with open(json_path, "w", encoding="utf-8") as f:
json.dump(rows, f, indent=2, ensure_ascii=False)
print(f"Converted {len(rows)} rows to JSON")
Note: all values from a CSV are strings. If you need numbers or booleans, you'll need a schema or type-inference step:
import re
def infer_type(value: str):
if value.lower() in ("true", "yes"): return True
if value.lower() in ("false", "no"): return False
if value == "": return None
try: return int(value)
except ValueError: pass
try: return float(value)
except ValueError: pass
return value
rows = [{k: infer_type(v) for k, v in row.items()} for row in rows]
Summary
JSON-to-CSV conversion is straightforward for flat arrays of objects. The complexity comes from nested structures — use pd.json_normalize in Python data pipelines and json2csv with flatten: true in Node.js. Always handle quoting for values that contain commas, double quotes, or newlines.
Before converting, use the JSON Formatter to inspect your source structure and AI JSONMedic to repair any malformed input — a clean JSON source produces a clean CSV every time.
FAQ
What is the easiest way to convert JSON to CSV?
In Python: pd.json_normalize(data).to_csv('output.csv', index=False) — handles nested objects by flattening keys with dot notation. In Node.js: npm install json2csv then const { parse } = require('json2csv'); const csv = parse(data). For one-off conversions, paste into an online converter and download the result.
How do I convert nested JSON to CSV?
Nested objects must be flattened before conversion. Python's pd.json_normalize() does this automatically — {"user": {"name": "Alice"}} becomes a user.name column. In JavaScript, manually flatten first: data.map(item => ({ ...item, userName: item.user?.name })) then drop the original nested key. Arrays within objects need to be either excluded, joined as strings, or exploded into separate rows.
Why does my CSV have extra quotes around values?
This is correct behavior — the CSV spec requires values containing commas, double quotes, or newlines to be wrapped in double quotes. If a value itself contains a double quote, it's escaped as "". Most CSV parsers handle this transparently. If you're seeing broken output, your CSV parser may not be RFC 4180 compliant.
How do I handle JSON arrays that contain arrays in CSV conversion?
Arrays within objects have no natural CSV representation. Options: (1) join array values as a delimited string ("tag1|tag2|tag3" in a single cell), (2) explode the array so each element becomes a separate row (one row per tag), or (3) exclude the array column entirely. The right choice depends on how the CSV will be consumed downstream.
Can I convert JSON to CSV without installing any libraries?
In Python: import json, csv — both are in the standard library. Read the JSON, use csv.DictWriter to write rows, and use fieldnames = data[0].keys() for the header. This works for flat arrays of objects. For nested data, use pd.json_normalize() from pandas which requires installation but handles the flattening automatically.
Still dealing with broken JSON?
Paste it in and get it fixed in under 1 second — free, no signup, no install. Works with ChatGPT, Claude, n8n, and any AI output.
Fix My JSON Free →Related Articles