Convert JSON to XML: Root Elements, Attributes, and Arrays

XML needs a single root and represents lists as repeated elements. Map @-prefixed keys to attributes and #text to element text for a reversible conversion.

Choosing a Root Element

XML 1.0 requires exactly one root element — the document's outermost open tag must match the document's outermost close tag. If the JSON has a single top-level key, use it as the root; otherwise wrap everything in a synthetic root element.

// JSON — has a single top-level key
{ "user": { "name": "Ada", "year": 1815 } }
<?xml version="1.0" encoding="UTF-8"?>
<user>
  <name>Ada</name>
  <year>1815</year>
</user>
// JSON — no single top-level key, so wrap
{ "name": "Ada", "year": 1815 }
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <name>Ada</name>
  <year>1815</year>
</root>

Paste any of these into the JSON to XML converter to see the transformation, or click To JSON in the reverse direction to round-trip.

Attributes and Text Content

The standard JSON-XML convention treats keys prefixed with @ as attributes and a #text key as the element's text content. The same convention is used by xml2js, fast-xml-parser, and most JSON-XML libraries, so the conversion round-trips:

{
  "user": {
    "@id": "42",
    "@active": "true",
    "name": "Ada",
    "bio": { "#text": "Mathematician", "@lang": "en" }
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<user id="42" active="true">
  <name>Ada</name>
  <bio lang="en">Mathematician</bio>
</user>

Without the @/#text convention, attributes and element text get the same JSON representation — the conversion loses the distinction and is no longer reversible.

Arrays Become Repeated Elements

A JSON array does not become a single element. Each item becomes a separate element with the same tag name — XML represents lists as repetition:

{
  "users": {
    "user": [
      { "name": "Ada" },
      { "name": "Alan" },
      { "name": "Grace" }
    ]
  }
}
<?xml version="1.0" encoding="UTF-8"?>
<users>
  <user><name>Ada</name></user>
  <user><name>Alan</name></user>
  <user><name>Grace</name></user>
</users>

A single-item array and a single object can't be distinguished in XML on read — most parsers default to "object if one, array if many," which makes round-trips ambiguous. If you control both sides, configure the parser to always treat a known tag name as an array.

Escaping

Five characters need special treatment in XML; the first three in element text, plus the two quote characters in attribute values:

Character In element text In attribute value
& &amp; &amp;
< &lt; &lt;
> &gt; (recommended) &gt; (recommended)
" (no escape needed) &quot; if attribute uses "
' (no escape needed) &apos; if attribute uses '

Skip these and the parser will either reject the document or silently misread it. Prepend the XML declaration (<?xml version="1.0" encoding="UTF-8"?>) so the output is a complete, parser-ready document.

Namespaces and Prefixes

XML namespaces appear as xmlns or xmlns:prefix attributes and qualify element and attribute names. In a JSON-friendly form, treat them as ordinary @xmlns / @xmlns:prefix attribute keys and keep prefixed names like soap:Envelope as the JSON key — most converters preserve the prefix verbatim:

{
  "soap:Envelope": {
    "@xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/",
    "soap:Body": { "GetWeather": { "City": "London" } }
  }
}
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body><GetWeather><City>London</City></GetWeather></soap:Body>
</soap:Envelope>

Self-Closing vs Explicit Empty Elements

An empty JSON value maps to either a self-closing tag (<note/>) or a paired empty tag (<note></note>). Both are equivalent XML, but downstream parsers occasionally mishandle one form — prefer self-closing for compact output and explicit pairs when the consumer is known to be strict (some legacy SOAP / SAML implementations are picky).

Round-Trip Considerations

Several XML facts have no JSON equivalent and are dropped during conversion. Plan the round trip with these in mind:

XML fact What happens in JSON
Attribute order Lost (JSON object keys are unordered in spec, though most parsers preserve insertion order in practice)
Comments (<!-- … -->) Dropped
Processing instructions (<?xml-stylesheet … ?>) Dropped
CDATA sections Collapsed into the element's text (escaping behavior shifts)
Empty element vs missing element Both can serialize as null or omitted; the distinction is lost
Mixed content (text interleaved with child elements) Hard to represent — most converters concatenate or warn

For data interchange these losses are usually fine; never use round-tripped XML where signature canonicalization (XML-DSig, SAML, SOAP-Sec) matters — re-serializing through JSON breaks the canonical byte sequence.

Common JSON-XML Converters

  • JavaScript / TypeScriptfast-xml-parser (the most maintained option in 2026) and xml2js (legacy but widely used)
  • Pythonxmltodict (uses the @/#text convention by default)
  • Goencoding/xml (stdlib; struct-tag driven, less flexible for arbitrary JSON)
  • BrowserDOMParser for parsing, XMLSerializer for serializing — built-in, no dependency

See also

This guide handles one format boundary. The hub lists every JSON ↔ neighbor-format conversion with its standard and edge cases.

Sources

Last reviewed June 2026.