DEV Community

Deepak Kumar
Deepak Kumar

Posted on Originally published at jsonformatterhub.com

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.

Contents: JSON to CSV · JSON to XML · JSON to YAML · Which Format to Choose

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 }
]
Enter fullscreen mode Exit fullscreen mode

Target CSV:

id,name,email,score,active
1,Alice,alice@example.com,98.5,true
2,Bob,bob@example.com,87,false
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode
// 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);
Enter fullscreen mode Exit fullscreen mode

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:

{
  "user": {
    "id": 42,
    "name": "Alice",
    "roles": ["admin", "editor"],
    "address": { "city": "London", "country": "UK" }
  }
}
Enter fullscreen mode Exit fullscreen mode
<?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>
Enter fullscreen mode Exit fullscreen mode

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, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;');
}

const xml = '<?xml version="1.0" encoding="UTF-8"?>\n' +
  jsonToXml(JSON.parse(jsonString), 'root');
Enter fullscreen mode Exit fullscreen mode

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())
Enter fullscreen mode Exit fullscreen mode

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: &&amp;, <&lt;, >&gt;
  • 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:

{
  "server": {
    "host": "localhost",
    "port": 8080,
    "debug": true,
    "allowed_hosts": ["example.com", "api.example.com"]
  }
}
Enter fullscreen mode Exit fullscreen mode
server:
  host: localhost
  port: 8080
  debug: true
  allowed_hosts:
    - example.com
    - api.example.com
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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
    )
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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!"
Enter fullscreen mode Exit fullscreen mode

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? JSON Formatter Hub's Export button converts JSON to CSV, XML, and YAML directly client-side — no data sent to a server, using the same logic shown in this article.

Top comments (0)