Free online JSON formatter, validator, and developer tools.
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.
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.
[
{
"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
}
]
id,name,email,score,active
1,Alice,alice@example.com,98.5,true
2,Bob,bob@example.com,87,false
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();
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)
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);
"London, UK""He said ""hello"""true, false, and empty string are all strings in the output\r\n (CRLF) for maximum compatibility with Excel on WindowsXML 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.
| 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 |
// 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>
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');
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())
"1stItem" becomes an invalid XML tag. You'll need to prefix it (<item_1stItem>) or sanitize keys.& โ &, < โ <, > โ ><item>.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.
// 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
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);
# 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 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
# YAML 1.1 interprets leading zeros as octal:
permissions: 0755 # becomes integer 493 in YAML 1.1!
# Fix: quote it
permissions: "0755"
# 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
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.
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!"
| 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 |
| 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 ~ |
Our JSON Formatter tool includes an Export button that converts your JSON to CSV, XML, and YAML directly in your browser. No data is sent to a server โ the conversion happens entirely client-side using the same logic shown in this article. Paste your JSON, click Format, then use the Export dropdown to download in your chosen format.
Use the JSON Formatter Hub to format, validate, and fix your JSON right now โ free and fully browser-based.