Why Convert JSON to CSV?
JSON is the standard data format for APIs and web applications, but it is not the best format for every task. When you need to analyze data in a spreadsheet, import records into a database, or share data with non-technical stakeholders, CSV (Comma-Separated Values) is often the better choice.
CSV files open natively in Excel, Google Sheets, and every data analysis tool. They are flat, tabular, and universally supported. Converting JSON to CSV bridges the gap between modern APIs and traditional data workflows.
Basic Conversion: Flat Objects
The simplest case is a JSON array of flat objects — objects with no nesting, where every value is a string, number, boolean, or null. Each object becomes a row, and the object keys become column headers.
Given this JSON input:
[
{ "name": "Alice", "age": 30, "city": "New York" },
{ "name": "Bob", "age": 25, "city": "London" },
{ "name": "Charlie", "age": 35, "city": "Tokyo" }
]
The CSV output is:
name,age,city Alice,30,New York Bob,25,London Charlie,35,Tokyo
The first row contains the headers (the union of all keys across all objects), and each subsequent row contains the corresponding values. If an object is missing a key that other objects have, the cell is left empty.
Handling Nested Objects
Real-world JSON often contains nested objects. For example, an API response might include an address object with city, state, and zip fields. Since CSV is a flat format, you need to flatten these nested structures.
The standard approach is dot notation: concatenate parent and child keys with a dot separator. An object like {"address": {"city": "NYC", "zip": "10001"}} becomes two columns: address.city and address.zip.
Example input:
[
{
"name": "Alice",
"address": { "city": "NYC", "state": "NY" }
},
{
"name": "Bob",
"address": { "city": "London", "state": null }
}
]
Flattened CSV output:
name,address.city,address.state Alice,NYC,NY Bob,London,
This approach works recursively for any depth of nesting. A key like user.address.street.line1 is perfectly valid as a CSV column header.
Handling Arrays Within Objects
Arrays within objects present a different challenge. A field like "tags": ["dev", "lead", "remote"] cannot be split into separate columns because the number of elements varies across rows.
The most common solution is to join array elements with a delimiter — typically a semicolon (;) since commas would conflict with the CSV format. The example above becomes dev;lead;remote in a single CSV cell.
If the array contains objects rather than primitives, you can either flatten each object into a JSON string or, if the structure is consistent, create numbered columns like items[0].name, items[1].name. The semicolon-join approach is simpler and works well for most use cases.
Edge Cases and Pitfalls
Several edge cases can trip you up when converting JSON to CSV:
- Values containing commas: If a string value contains a comma, the entire value must be wrapped in double quotes. For example,
"New York, NY"becomes"New York, NY"in the CSV output. - Values containing double quotes: Double quotes inside a value must be escaped by doubling them. The value
She said "hello"becomes"She said ""hello"""in CSV. - Newlines in values: If a value contains a newline character, it must also be wrapped in double quotes to prevent it from being interpreted as a new row.
- Missing keys: Not all objects in a JSON array will necessarily have the same keys. Your converter should handle this gracefully by outputting an empty cell for missing values.
- Null and boolean values: Decide how to represent
null(empty cell or the string "null") and booleans (true/falseor1/0) based on your downstream requirements. - Unicode and encoding: Always save CSV files as UTF-8 to preserve special characters. Some spreadsheet applications require a BOM (byte order mark) to correctly detect UTF-8 encoding.
Converting Programmatically
In JavaScript, you can convert JSON to CSV in a few lines. Extract the headers from all objects, then map each object to a row of comma-separated values:
function jsonToCSV(data) {
const headers = [...new Set(data.flatMap(Object.keys))];
const rows = data.map(obj =>
headers.map(h => {
const val = obj[h] ?? '';
const str = String(val);
return str.includes(',') || str.includes('"')
? '"' + str.replace(/"/g, '""') + '"'
: str;
}).join(',')
);
return [headers.join(','), ...rows].join('\n');
}
In Python, the built-in csv module handles escaping automatically:
import csv, io, json
def json_to_csv(data):
headers = list({k for obj in data for k in obj})
output = io.StringIO()
writer = csv.DictWriter(output, fieldnames=headers)
writer.writeheader()
writer.writerows(data)
return output.getvalue()
For quick, one-off conversions without writing code, use an online tool like ToolPlex's JSON Converter — paste your JSON and download the CSV instantly.