How to Convert Nested JSON to CSV: A Complete Guide
Step-by-step tutorial on handling complex nested JSON structures when converting to CSV format.
Introduction
Converting nested JSON to CSV is a common challenge in data processing. While CSV is a flat, tabular format, JSON can represent complex hierarchical structures. This guide will walk you through strategies and techniques for flattening nested JSON data into CSV format while preserving as much information as possible.
Understanding the Challenge
The fundamental issue is that CSV is a two-dimensional format (rows and columns), while JSON can have multiple levels of nesting. Before converting, you need to decide how to represent nested data in a flat structure.
// Example: Nested JSON structure
{
"users": [
{
"id": 1,
"name": "John Doe",
"address": {
"street": "123 Main St",
"city": "Boston",
"country": "USA"
},
"orders": [
{ "orderId": "A001", "amount": 99.99 },
{ "orderId": "A002", "amount": 149.99 }
]
}
]
}Common Nested Structures:
- Objects within objects (nested properties)
- Arrays of objects (one-to-many relationships)
- Arrays of primitive values
- Mixed nesting (objects containing arrays containing objects)
- Deeply nested data (3+ levels)
Strategy 1: Flattening Nested Objects
The simplest approach for nested objects is to flatten them using dot notation or underscore separators. This works well for objects that are always present and have a consistent structure.
// Flattening nested objects
function flattenObject(obj, prefix = '') {
const flattened = {};
for (const [key, value] of Object.entries(obj)) {
const newKey = prefix ? `${prefix}_${key}` : key;
if (value && typeof value === 'object' && !Array.isArray(value)) {
// Recursively flatten nested objects
Object.assign(flattened, flattenObject(value, newKey));
} else if (Array.isArray(value)) {
// Handle arrays separately
flattened[newKey] = JSON.stringify(value);
} else {
flattened[newKey] = value;
}
}
return flattened;
}
// Example usage
const user = {
id: 1,
name: "John Doe",
address: {
street: "123 Main St",
city: "Boston",
country: "USA"
}
};
const flat = flattenObject(user);
// Result:
// {
// id: 1,
// name: "John Doe",
// address_street: "123 Main St",
// address_city: "Boston",
// address_country: "USA"
// }When to Use This Strategy:
- Nested objects have a fixed, predictable structure
- Each parent object has exactly one nested object
- The nesting level is shallow (1-2 levels)
- You want to preserve all data in a single row
Strategy 2: Creating Multiple Rows
When you have arrays of objects, you can create multiple CSV rows - one for each array element. This is useful for one-to-many relationships where you want to preserve the relationship between parent and child records.
// Creating multiple rows for array items
function expandArrays(data) {
const rows = [];
data.forEach(item => {
const baseData = { ...item };
delete baseData.orders; // Remove the array field
if (item.orders && item.orders.length > 0) {
// Create one row per order
item.orders.forEach(order => {
rows.push({
...baseData,
orderId: order.orderId,
orderAmount: order.amount
});
});
} else {
// If no orders, still include the base data
rows.push({ ...baseData, orderId: null, orderAmount: null });
}
});
return rows;
}
// Example result:
// [
// { id: 1, name: "John", orderId: "A001", orderAmount: 99.99 },
// { id: 1, name: "John", orderId: "A002", orderAmount: 149.99 }
// ]When to Use This Strategy:
- You have one-to-many relationships (e.g., user → orders)
- Each array element is important and needs its own row
- You're comfortable with data duplication
- The array size is manageable (not hundreds of items)
Strategy 3: Serializing Complex Data
For complex or variable structures, you can serialize nested data as JSON strings within CSV cells. This preserves all information but makes the data less accessible for analysis.
// Serializing complex nested data
function serializeNested(data) {
return data.map(item => {
const result = {
id: item.id,
name: item.name,
// Serialize nested objects/arrays as JSON strings
address: JSON.stringify(item.address),
orders: JSON.stringify(item.orders),
metadata: JSON.stringify(item.metadata)
};
return result;
});
}
// CSV will look like:
// id,name,address,orders
// 1,"John Doe","{""street"":""123 Main St"",""city"":""Boston""}","[{""orderId"":""A001""}]"When to Use This Strategy:
- Nested structure is highly variable or unpredictable
- You need to preserve exact structure for later processing
- The data won't be analyzed directly in spreadsheet software
- You're using CSV as a transport format, not for analysis
Strategy 4: Creating Separate CSV Files
For complex data with multiple one-to-many relationships, consider creating separate CSV files for each entity type, similar to database normalization.
// Creating separate CSV files for related data
function splitIntoTables(data) {
const users = [];
const orders = [];
const addresses = [];
data.forEach(user => {
// Users table
users.push({
userId: user.id,
name: user.name
});
// Addresses table
if (user.address) {
addresses.push({
userId: user.id,
street: user.address.street,
city: user.address.city,
country: user.address.country
});
}
// Orders table
if (user.orders) {
user.orders.forEach(order => {
orders.push({
orderId: order.orderId,
userId: user.id,
amount: order.amount
});
});
}
});
return { users, orders, addresses };
}
// Results in three separate CSV files:
// users.csv, orders.csv, addresses.csv
// with userId as the foreign keyWhen to Use This Strategy:
- You have multiple levels of nested arrays
- Data volume is large and duplication would be wasteful
- You need to maintain referential integrity
- Data will be imported into a relational database
- You want to avoid data duplication
Best Practices and Tips
Regardless of which strategy you choose, keep these best practices in mind to ensure your conversion is reliable and maintainable.
Important Considerations:
- Document your flattening strategy in code comments
- Handle missing or null nested values gracefully
- Test with real-world data samples, not just ideal cases
- Consider the end use case when choosing a strategy
- Validate output to ensure no data loss occurred
- Use consistent naming conventions for flattened fields
- Implement proper CSV escaping for serialized JSON strings
- Consider memory usage for large datasets
Choosing the Right Strategy
The best approach depends on your specific use case. Use flattening for simple nested objects, create multiple rows for one-to-many relationships you need to analyze, serialize for preservation of complex structures, and create separate files for normalized data. Often, a combination of these strategies works best. Always validate your output and ensure the conversion meets the needs of your downstream processes.
Handle Nested JSON with Ease
Use our JSON to CSV tool to automatically handle nested structures without manual coding.