JSON Formatter Hub

Free online JSON formatter, validator, and developer tools.

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);

CSV gotchas

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
StringText node content
NumberText node content
BooleanText 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

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 SheetsCSVNative import; no programming required
Flat tabular data exportCSVSmallest file size, widest tool support
Enterprise integrations, SOAP servicesXMLSchema validation (XSD), namespace support
Document stores, content with mixed structureXMLMixed content model (text + elements)
Kubernetes / Docker Compose configYAMLEcosystem standard
CI/CD pipelines (GitHub Actions, GitLab CI)YAMLEcosystem standard
Config files developers will hand-editYAMLComments, readability, less syntax noise
API responses, web data transferJSONNative browser support, fast parsing
Deeply nested hierarchical dataJSON or XMLCSV cannot represent nesting

Quick Reference: Format Capabilities

Feature JSON CSV XML YAML
Nested structureYesNoYesYes
ArraysYesNo (workaround)YesYes
CommentsNoNoYesYes
Schema validationJSON SchemaNo standardXSD, DTDNo standard
Human-editableModerateYes (simple)VerboseYes
Spreadsheet importManualNativeManualManual
Browser nativeYesNoYes (DOM)No
Null valuesnullEmpty stringxsi:nilnull or ~

Converting in the Browser

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.

Further Reading

Use the JSON Formatter Hub to format, validate, and fix your JSON right now โ€” free and fully browser-based.