JSON Formatter Hub
How to Convert JSON to CSV, XML, and YAML (With Examples)
JSON is the universal interchange format, but that doesn't mean every tool that needs your data speaks JSON. Spreadsheet users want CSV. Enterprise integrations often expect XML. Infrastructure engineers configure in YAML. Knowing how to convert JSON to each format โ and the traps to avoid along the way โ is a daily developer skill.
1. JSON to CSV
CSV works naturally for flat arrays of objects โ each object becomes a row, each key becomes a column header. The difficulty arises with nested objects and arrays.
The source JSON
[
{
"id": 1,
"name": "Alice",
"email": "alice@example.com",
"score": 98.5,
"active": true
},
{
"id": 2,
"name": "Bob",
"email": "bob@example.com",
"score": 87.0,
"active": false
}
]
Target CSV
id,name,email,score,active
1,Alice,alice@example.com,98.5,true
2,Bob,bob@example.com,87,false
JavaScript: JSON to CSV
function jsonToCsv(jsonArray) {
if (!Array.isArray(jsonArray) || jsonArray.length === 0) {
throw new Error('Input must be a non-empty array');
}
// Collect all unique keys across all objects (handles sparse data)
const headers = [...new Set(jsonArray.flatMap(obj => Object.keys(obj)))];
const escape = (value) => {
if (value === null || value === undefined) return '';
const str = String(value);
// Wrap in quotes if contains comma, quote, or newline
if (str.includes(',') || str.includes('"') || str.includes('\n')) {
return '"' + str.replace(/"/g, '""') + '"';
}
return str;
};
const rows = jsonArray.map(obj =>
headers.map(h => escape(obj[h])).join(',')
);
return [headers.join(','), ...rows].join('\n');
}
// Usage
const data = JSON.parse(jsonString);
const csv = jsonToCsv(data);
// Download in browser
const blob = new Blob([csv], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'data.csv';
a.click();
Python: JSON to CSV
import csv
import json
import io
def json_to_csv(json_data: list[dict]) -> str:
if not json_data:
return ''
# Collect all keys to handle sparse objects
all_keys = list(dict.fromkeys(
key for obj in json_data for key in obj.keys()
))
output = io.StringIO()
writer = csv.DictWriter(
output,
fieldnames=all_keys,
extrasaction='ignore',
restval='' # value for missing fields
)
writer.writeheader()
writer.writerows(json_data)
return output.getvalue()
# Usage
with open('data.json') as f:
data = json.load(f)
csv_text = json_to_csv(data)
with open('output.csv', 'w', newline='', encoding='utf-8') as f:
f.write(csv_text)
Flattening nested JSON for CSV
CSV has no concept of nested structure. If your JSON objects contain nested objects or arrays, you must decide how to flatten them.
// Source: nested JSON
{
"id": 1,
"name": "Alice",
"address": {
"city": "London",
"country": "UK"
},
"tags": ["admin", "editor"]
}
// After flattening (dot-notation keys for nested objects, joined for arrays)
{
"id": 1,
"name": "Alice",
"address.city": "London",
"address.country": "UK",
"tags": "admin|editor"
}
// JavaScript flatten function
function flattenObject(obj, prefix = '') {
return Object.keys(obj).reduce((acc, key) => {
const fullKey = prefix ? `${prefix}.${key}` : key;
const value = obj[key];
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
Object.assign(acc, flattenObject(value, fullKey));
} else if (Array.isArray(value)) {
acc[fullKey] = value.join('|'); // or JSON.stringify(value)
} else {
acc[fullKey] = value;
}
return acc;
}, {});
}
const flattened = data.map(obj => flattenObject(obj));
const csv = jsonToCsv(flattened);
To reverse this later, the CSV to JSON converter has a "Nested keys from dotted headers" option that turns address.city columns back into nested objects.
CSV gotchas
- Commas in values โ must be wrapped in double quotes:
"London, UK" - Quotes inside quoted values โ escaped by doubling:
"He said ""hello""" - Newlines in values โ wrap the entire value in quotes
- Boolean and null โ CSV has no native types;
true,false, and empty string are all strings in the output - Line endings โ use
\r\n(CRLF) for maximum compatibility with Excel on Windows
2. JSON to XML
XML is more expressive than JSON in some ways (attributes vs. elements, namespaces, comments) but the mapping from JSON to XML is lossy โ you have to make decisions about how to represent each JSON type.
The mapping rules
| JSON | XML Representation |
|---|---|
Object {"key": "val"} | <key>val</key> |
Array [1, 2, 3] | Repeated <item> elements |
| String | Text node content |
| Number | Text node content |
| Boolean | Text node: true or false |
null | <key xsi:nil="true" /> or empty element |
Example conversion
// Source JSON
{
"user": {
"id": 42,
"name": "Alice",
"roles": ["admin", "editor"],
"address": {
"city": "London",
"country": "UK"
}
}
}
// Target XML
<?xml version="1.0" encoding="UTF-8"?>
<root>
<user>
<id>42</id>
<name>Alice</name>
<roles>
<item>admin</item>
<item>editor</item>
</roles>
<address>
<city>London</city>
<country>UK</country>
</address>
</user>
</root>
JavaScript: JSON to XML
function jsonToXml(obj, rootElement = 'root', indent = 0) {
const pad = ' '.repeat(indent);
if (obj === null) return `${pad}<${rootElement}/>`;
if (typeof obj !== 'object') {
return `${pad}<${rootElement}>${escapeXml(String(obj))}</${rootElement}>`;
}
if (Array.isArray(obj)) {
return obj.map(item => jsonToXml(item, 'item', indent)).join('\n');
}
const children = Object.entries(obj)
.map(([key, val]) => jsonToXml(val, key, indent + 1))
.join('\n');
return `${pad}<${rootElement}>\n${children}\n${pad}</${rootElement}>`;
}
function escapeXml(str) {
return str
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const xml = '<?xml version="1.0" encoding="UTF-8"?>\n' +
jsonToXml(JSON.parse(jsonString), 'root');
Python: JSON to XML
import json
import xml.etree.ElementTree as ET
def dict_to_xml(data, parent):
if isinstance(data, dict):
for key, value in data.items():
child = ET.SubElement(parent, key)
dict_to_xml(value, child)
elif isinstance(data, list):
for item in data:
child = ET.SubElement(parent, 'item')
dict_to_xml(item, child)
elif data is None:
parent.set('xsi:nil', 'true')
else:
parent.text = str(data)
def json_to_xml(json_str: str, root_tag: str = 'root') -> str:
data = json.loads(json_str)
root = ET.Element(root_tag)
dict_to_xml(data, root)
ET.indent(root, space=' ') # Python 3.9+
return ET.tostring(root, encoding='unicode', xml_declaration=True)
xml_output = json_to_xml(open('data.json').read())
XML gotchas
- XML element names can't start with numbers. A JSON key like
"1stItem"becomes an invalid XML tag. You'll need to prefix it (<item_1stItem>) or sanitize keys. - XML element names can't contain spaces. JSON keys with spaces must be converted (e.g., replace with underscores).
- Special characters must be escaped:
&โ&,<โ<,>โ> - Array element naming is ambiguous. When a JSON array contains objects, what do you name each element? Convention: use the singular form of the parent key or
<item>.
3. JSON to YAML
JSON is a strict subset of YAML โ every valid JSON document is also valid YAML. Converting from JSON to YAML is about readability: YAML drops the quotes, braces, and brackets of JSON in favor of indentation-based structure.
Side-by-side comparison
// JSON
{
"server": {
"host": "localhost",
"port": 8080,
"debug": true,
"allowed_hosts": ["example.com", "api.example.com"]
}
}
# YAML equivalent
server:
host: localhost
port: 8080
debug: true
allowed_hosts:
- example.com
- api.example.com
JavaScript: JSON to YAML
Use the js-yaml library โ converting YAML by hand is error-prone.
// npm install js-yaml
const yaml = require('js-yaml');
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('data.json', 'utf8'));
const yamlStr = yaml.dump(data, {
indent: 2,
lineWidth: -1, // disable line wrapping
noRefs: true // don't use YAML aliases for repeated values
});
fs.writeFileSync('output.yaml', yamlStr);
Python: JSON to YAML
# pip install pyyaml
import json
import yaml
with open('data.json') as f:
data = json.load(f)
with open('output.yaml', 'w') as f:
yaml.dump(
data,
f,
default_flow_style=False, # block style, not inline
allow_unicode=True,
sort_keys=False # preserve key order
)
YAML gotchas when converting from JSON
The Norway problem (YAML 1.1)
# YAML 1.1 interprets these as booleans, not strings:
country: NO # parsed as false in YAML 1.1
flag: yes # parsed as true
status: on # parsed as true
# In JSON, these would have been quoted strings: "NO", "yes", "on"
# Fix: always quote ambiguous string values, or use YAML 1.2
Octal number ambiguity
# YAML 1.1 interprets leading zeros as octal:
permissions: 0755 # becomes integer 493 in YAML 1.1!
# Fix: quote it
permissions: "0755"
Multiline strings
# Long strings in JSON:
{"description": "Line one\nLine two\nLine three"}
# YAML literal block scalar (preserves newlines):
description: |
Line one
Line two
Line three
# YAML folded scalar (newlines become spaces):
description: >
Line one
Line two
Line three
Anchors and aliases (YAML-only feature)
When converting from JSON to YAML, you lose the ability to use YAML anchors (&anchor) and aliases (*alias) to deduplicate repeated values โ those are written by hand or added by YAML-aware serializers. The conversion will duplicate the data.
Validating your YAML output
Always parse the YAML output back to verify it round-trips correctly:
import json
import yaml
original = json.loads(original_json)
converted = yaml.safe_load(yaml_output)
assert original == converted, "Round-trip failed!"
4. Which Format to Choose
| Use Case | Best Format | Reason |
|---|---|---|
| Spreadsheet analysis, Excel, Google Sheets | CSV | Native import; no programming required |
| Flat tabular data export | CSV | Smallest file size, widest tool support |
| Enterprise integrations, SOAP services | XML | Schema validation (XSD), namespace support |
| Document stores, content with mixed structure | XML | Mixed content model (text + elements) |
| Kubernetes / Docker Compose config | YAML | Ecosystem standard |
| CI/CD pipelines (GitHub Actions, GitLab CI) | YAML | Ecosystem standard |
| Config files developers will hand-edit | YAML | Comments, readability, less syntax noise |
| API responses, web data transfer | JSON | Native browser support, fast parsing |
| Deeply nested hierarchical data | JSON or XML | CSV cannot represent nesting |
Quick Reference: Format Capabilities
| Feature | JSON | CSV | XML | YAML |
|---|---|---|---|---|
| Nested structure | Yes | No | Yes | Yes |
| Arrays | Yes | No (workaround) | Yes | Yes |
| Comments | No | No | Yes | Yes |
| Schema validation | JSON Schema | No standard | XSD, DTD | No standard |
| Human-editable | Moderate | Yes (simple) | Verbose | Yes |
| Spreadsheet import | Manual | Native | Manual | Manual |
| Browser native | Yes | No | Yes (DOM) | No |
| Null values | null | Empty string | xsi:nil | null or ~ |
Converting in the Browser
Our JSON Formatter tool has an Export/Download menu that converts your JSON to CSV, XML, and YAML directly in your browser. No data is sent to a server. It is a quick, simplified export, though, and it does not follow every rule in this article: CSV export writes nested objects and arrays as JSON text inside a single cell instead of flattening them into dotted columns; XML output always has a single root element (a key is the root when the object has exactly one top-level key, and anything else is wrapped in <root>), names array items by dropping a trailing "s" from the key (tags becomes tag) or adding "_item" (book becomes book_item, status becomes status_item), writes null as an empty element with xsi:nil="true", and writes empty arrays and objects as empty elements, which cannot be told apart when the XML is read back; and YAML export quotes strings that a parser would read as another type (such as "yes", "123", a date or an empty string), writes multi-line strings as double-quoted text with \n escapes, and writes empty lists and objects as [] and {}, so it reads back as the same JSON. Check the gotchas above and the output if your target parser is strict. Paste your JSON, open Export/Download, and pick a format.
Going the other way, the CSV to JSON converter turns a CSV file back into JSON, including dotted headers such as address.city into nested objects. For XML, the XML to JSON converter does the same job, with options for attributes, repeated elements and CDATA. For YAML files, the YAML to JSON converter reads anchors, merge keys and multi-document files, in YAML 1.2 or 1.1 mode. For a spreadsheet, the Excel to JSON converter reads .xlsx, .xls and .ods files and lets you choose the sheet, the header row and how dates and formulas are written. To move between a JSON array and JSON Lines, the JSON to JSONL converter reads and writes both.
Further Reading
Use the JSON Formatter Hub to format, validate, and fix your JSON right now โ free and fully browser-based.
