How JSON objects map to XML elements
Each JSON key becomes an XML element tag and its value becomes the element's content. Because XML requires a single root node and a JSON object has no name of its own, the converter wraps everything in a <root> element and adds an XML declaration on the first line.
{
"name": "Alice",
"age": 30,
"active": true
}<?xml version="1.0" encoding="UTF-8"?>
<root>
<name>Alice</name>
<age>30</age>
<active>true</active>
</root>Arrays become repeated sibling elements
XML has no native array type, so the standard convention is to repeat the same tag for each item. A colors array does not produce an index — it produces three<colors> siblings sharing the key name.
{
"colors": ["red", "green", "blue"]
}<?xml version="1.0" encoding="UTF-8"?>
<root>
<colors>red</colors>
<colors>green</colors>
<colors>blue</colors>
</root>A top-level array has no key at all, so each element is wrapped in a generic<item> tag inside <root>:
[
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" }
]<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>
<id>1</id>
<name>Alice</name>
</item>
<item>
<id>2</id>
<name>Bob</name>
</item>
</root>Attributes vs. child elements
JSON has only keys and values, all at the same level — there is no attribute concept. A generic converter therefore maps every key to a child element and never to an attribute. If your target XML expects attributes (for example active="true" on a <user>), you need a converter that recognizes an attribute-prefix convention — commonly an @ on the key — or you post-process the output.
// Default: everything is a child element
{ "user": { "id": 7, "active": true } }
<root>
<user>
<id>7</id>
<active>true</active>
</user>
</root>
// With an "@" attribute convention (converter-specific)
{ "user": { "@id": 7, "name": "Alice" } }
<root>
<user id="7">
<name>Alice</name>
</user>
</root>Null values and invalid tag names
JSON null has no XML equivalent, so it is emitted as a self-closing element that is present but empty. Separately, XML tag names cannot start with a digit and may only contain letters, digits, hyphens, underscores, and periods — so an invalid key like "1" is sanitized with an underscore prefix.
{
"middleName": null,
"1": "first",
"order-id": 42
}<root>
<middleName/>
<_1>first</_1>
<order-id>42</order-id>
</root>Escaping special characters in values
Five characters must be escaped inside element content or the document is no longer well-formed. The converter does this automatically, but knowing the rules helps when you read the output:& → &, < → <,> → >, " → ", and' → '.
{
"title": "Tom & Jerry <fast>",
"quote": "She said \"hi\""
}<root>
<title>Tom & Jerry <fast></title>
<quote>She said "hi"</quote>
</root>Convert JSON to XML in code
When you need conversion in a script or build pipeline instead of the browser tool:
# Python — dicttoxml, set a custom root and drop array index attributes
import json
from dicttoxml import dicttoxml
data = json.load(open("data.json"))
xml = dicttoxml(data, custom_root="root", attr_type=False)
print(xml.decode())// Node.js — xml-js, with compact input and pretty output
const { js2xml } = require('xml-js')
const data = require('./data.json')
console.log(js2xml({ root: data }, { compact: true, spaces: 2 }))// Node.js — xmlbuilder2, fluent API with explicit declaration
const { create } = require('xmlbuilder2')
const data = require('./data.json')
const doc = create({ version: '1.0', encoding: 'UTF-8' }, { root: data })
console.log(doc.end({ prettyPrint: true }))Common JSON to XML mistakes
- Expecting array items to carry an index — they collapse into identically named repeated tags.
- Assuming the output is schema-valid; automatic conversion is only well-formed, not XSD-valid.
- Wanting attributes from plain keys without an
@-prefix or custom mapping. - Leaving the default
<root>name when a target API expects a specific root tag.
For a step-by-step walkthrough with more examples, see the JSON to XML tutorial. To go the other way, use XML to JSON.