To convert JSON to XML cleanly, choose one document root, map object keys to child elements, map @ keys to attributes, put element text in #text, repeat child elements for arrays, sanitize invalid tag names, and escape XML-sensitive characters. You can do that in the JSON to XML converter, with JavaScript's fast-xml-parser, or with Python's xmltodict or xml.etree.ElementTree.
The tricky part is not the syntax. It is the data model. JSON says "this is an object with keys"; XML says "this is a document with one root element, ordered children, attributes, namespaces, and text nodes." A converter has to fill in decisions JSON never recorded. If you make those decisions deliberately, the XML is boring in the best way: it validates, it round-trips, and the system receiving it does not need a custom rescue parser.
The Short Version
Use this checklist before you convert anything important:
| Situation | Use this rule |
|---|---|
| Your JSON has one top-level key | Use that key as the XML root. |
| Your JSON has multiple top-level keys | Wrap them in a named root such as <order>, <payload>, or <root>. |
| Your JSON is a top-level array | Wrap it, then repeat one child name such as <item> or <order>. |
| A value describes an identifier, code, language, or unit | Consider an attribute such as @id or @currency. |
| A value is repeatable, structured, long, or user-authored text | Keep it as a child element. |
| An element has attributes and text | Store the text under #text. |
| A key contains spaces, starts with a digit, or uses punctuation | Rename or sanitize it before writing XML. |
| A consumer validates against XSD | Follow the schema's element names, order, namespace URI, and text format first. |
| You need JSON -> XML -> JSON to be predictable | Document the mapping convention and test a round trip. |
For quick, non-sensitive payloads, paste the JSON into JSON to XML. For production integrations, write the mapping down next to the contract. "The converter did whatever it did" is not a great contract.
A Realistic Example
Here is a small order payload. It has attributes, repeated items, numbers, text that needs escaping, and one wrapper element around the list:
{
"order": {
"@id": "ord_1001",
"@currency": "USD",
"customer": {
"name": "Ada Lovelace",
"email": "ada@example.com"
},
"items": {
"item": [
{
"@sku": "BK-001",
"name": "Notebook",
"quantity": 2,
"price": 9.99
},
{
"@sku": "PN-010",
"name": "Pen",
"quantity": 3,
"price": 1.5
}
]
},
"note": {
"#text": "Ship after 5pm & call first"
}
}
}
A conventional XML result looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<order id="ord_1001" currency="USD">
<customer>
<name>Ada Lovelace</name>
<email>ada@example.com</email>
</customer>
<items>
<item sku="BK-001">
<name>Notebook</name>
<quantity>2</quantity>
<price>9.99</price>
</item>
<item sku="PN-010">
<name>Pen</name>
<quantity>3</quantity>
<price>1.5</price>
</item>
</items>
<note>Ship after 5pm & call first</note>
</order>
Notice the small but important details:
orderis the single XML document root.@id,@currency, and@skubecame attributes.items.itembecame repeated<item>elements.- Numbers became text. XML does not remember that
9.99was a JSON number. - The ampersand in the note became
&.
That is the mental model for most JSON to XML work.
Root Element Rules
An XML document has exactly one document element. A JSON document can be an object, array, string, number, boolean, or null, so the converter may need to invent a wrapper.
When the JSON has one top-level object key, keep it:
{ "message": { "to": "Ada", "from": "Bob" } }
<message>
<to>Ada</to>
<from>Bob</from>
</message>
When the JSON has several top-level keys, pick a domain root instead of leaving the tool to choose a vague <root>:
{ "to": "Ada", "from": "Bob", "body": "Hello" }
<message>
<to>Ada</to>
<from>Bob</from>
<body>Hello</body>
</message>
When the JSON is a top-level array, XML still needs a wrapper:
[
{ "id": 1, "name": "Ada" },
{ "id": 2, "name": "Bob" }
]
<users>
<user>
<id>1</id>
<name>Ada</name>
</user>
<user>
<id>2</id>
<name>Bob</name>
</user>
</users>
If you control the receiving system, use a root name that describes the payload: <invoice>, <catalog>, <users>, <event>. If you do not control it, copy the root element from the target API, SOAP envelope, RSS spec, sitemap format, or XSD.
Attributes vs Child Elements
Attributes are best for compact metadata about an element:
{
"price": {
"@currency": "USD",
"#text": "9.99"
}
}
<price currency="USD">9.99</price>
Use attributes when the value is:
- An ID, code, unit, language, version, or boolean flag.
- Not repeatable on the same element.
- Not structured.
- Not long human-authored text.
Use child elements when the value may repeat, contain nested fields, hold prose, or need validation as its own object:
<customer id="cus_123">
<name>Ada Lovelace</name>
<email>ada@example.com</email>
</customer>
The limitation is practical and structural: attributes cannot contain child elements, and an element cannot have two attributes with the same name. Attribute order also should not carry business meaning. If order matters, use child elements.
The #text Convention
An XML element can have attributes and text at the same time:
<title lang="en">JSON to XML</title>
A JSON object needs a reserved key to represent that text. The common convention is #text:
{
"title": {
"@lang": "en",
"#text": "JSON to XML"
}
}
If the element has only text, you can usually keep the simpler JSON shape:
{ "title": "JSON to XML" }
The moment attributes enter the picture, switch to the object shape with #text. Mixing the two styles randomly is where round trips become annoying.
Arrays Become Repeated Elements
XML does not have a literal array syntax. The idiom is repetition:
{
"tags": {
"tag": ["json", "xml", "conversion"]
}
}
<tags>
<tag>json</tag>
<tag>xml</tag>
<tag>conversion</tag>
</tags>
For arrays of objects, repeat the item element:
{
"lineItems": {
"lineItem": [
{ "@sku": "BK-001", "quantity": 2 },
{ "@sku": "PN-010", "quantity": 3 }
]
}
}
<lineItems>
<lineItem sku="BK-001">
<quantity>2</quantity>
</lineItem>
<lineItem sku="PN-010">
<quantity>3</quantity>
</lineItem>
</lineItems>
The name of the repeated element matters. <items><item>... is acceptable for internal tools, but many integrations expect domain names such as <orders><order>... or <lineItems><lineItem>.... If an XSD is involved, do not guess. Use the exact names and order from the schema.
Names, Namespaces, and XSD Order
JSON keys can be almost anything. XML element and attribute names cannot. A key like "1st place" or "ship to" must be renamed or sanitized before it can become a tag:
| JSON key | Better XML name |
|---|---|
"1st place" |
<_1stPlace> or <firstPlace> |
"ship to" |
<shipTo> |
"order.total" |
<orderTotal> |
"@class" |
Attribute class, if that is intentional |
Be careful with colons. In XML, a colon normally separates a namespace prefix from a local name. "soap:Envelope" should mean a SOAP element, not just a fancy key name:
{
"soap:Envelope": {
"@xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/",
"soap:Body": {
"GetOrder": {
"id": "ord_1001"
}
}
}
}
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetOrder>
<id>ord_1001</id>
</GetOrder>
</soap:Body>
</soap:Envelope>
For schema-validated XML, child order is just as important as naming. JSON object order is usually treated as incidental in application code, but XML consumers often validate a strict sequence. If the schema says <id>, then <customer>, then <items>, emit that order even if the source JSON arrived differently.
Escaping, CDATA, and Invalid Characters
Text and attribute values need XML escaping. At minimum:
| Character | Escape |
|---|---|
& |
& |
< |
< |
> |
> |
" inside double-quoted attributes |
" |
' inside single-quoted attributes |
' |
So this JSON:
{ "message": "B & C are < D" }
becomes:
<message>B & C are < D</message>
CDATA can make embedded markup easier to read:
<snippet><![CDATA[<strong>Hello</strong>]]></snippet>
But CDATA is not a security feature and it is not a magic "store any string" box. The sequence ]]> cannot appear inside one CDATA section, and the receiving system still gets text content. For HTML snippets, logs, or Markdown, escaping is usually simpler and more predictable.
Also check for characters that XML 1.0 cannot represent. If your JSON contains raw control bytes from logs or terminal output, clean or encode them before building XML.
What Gets Lost During Conversion
JSON to XML is usually lossy unless you add a convention around the conversion.
Types are the first loss. XML stores text. The JSON number 42, the JSON string "42", and sometimes even a date-like string all become character data. A schema can validate that the text looks like an integer or date, but the XML itself does not carry the original JSON type.
null is the second loss. Teams usually choose one of three policies:
| JSON value | XML policy | What it means |
|---|---|---|
"middleName": null |
<middleName/> |
Field is present but empty. |
"middleName": null |
omit <middleName> |
Field is absent or not applicable. |
"middleName": null |
<middleName xsi:nil="true"/> |
Field is explicitly nil, usually under an XML Schema convention. |
Whitespace and mixed content are the third loss. XML can interleave text and child elements, like <p>Hello <b>Ada</b>.</p>. Plain JSON objects do not represent that gracefully without a special convention.
For important integrations, test the exact path you need:
- Convert JSON to XML.
- Validate the XML if an XSD exists.
- Parse the XML back to JSON using the reader your consumer uses.
- Compare the fields that must survive.
- Record the policy for arrays, attributes, nulls, types, namespaces, and text.
That sounds fussy, but it is cheaper than discovering that order IDs turned into numbers or optional fields disappeared after launch.
Convert JSON to XML in JavaScript
fast-xml-parser includes XMLBuilder, which builds XML from a JavaScript object. Configure the attribute and text-node convention instead of relying on whatever another tool used earlier.
import { XMLBuilder } from "fast-xml-parser";
const data = {
order: {
"@id": "ord_1001",
"@currency": "USD",
customer: {
name: "Ada Lovelace",
email: "ada@example.com"
},
items: {
item: [
{ "@sku": "BK-001", quantity: 2, price: 9.99 },
{ "@sku": "PN-010", quantity: 3, price: 1.5 }
]
},
note: {
"#text": "Ship after 5pm & call first"
}
}
};
const builder = new XMLBuilder({
ignoreAttributes: false,
attributeNamePrefix: "@",
textNodeName: "#text",
format: true,
suppressEmptyNode: false
});
const xml = builder.build(data);
console.log(xml);
Two production notes:
- Validate the JSON shape before building XML. A typo such as
"@curency"will still produce XML, just not the XML your partner expects. - Do not use string concatenation for XML. Let the builder escape text and attributes, especially when values come from users, logs, or API responses.
If you are creating namespace-heavy SOAP messages, a dedicated XML builder can be easier to control than a generic JSON-object mapper. The more the target XML looks like a document format rather than a data object, the more you should build it as XML directly.
Convert JSON to XML in Python
For dictionary-shaped data that already follows the @ and #text convention, xmltodict.unparse() is concise:
import xmltodict
data = {
"price": {
"@currency": "USD",
"#text": "9.99",
}
}
xml = xmltodict.unparse(data, pretty=True)
print(xml)
For long-lived code, xml.etree.ElementTree is often clearer because the mapping is explicit:
import xml.etree.ElementTree as ET
order = {
"id": "ord_1001",
"currency": "USD",
"customer": {"name": "Ada Lovelace", "email": "ada@example.com"},
"items": [
{"sku": "BK-001", "quantity": 2, "price": "9.99"},
{"sku": "PN-010", "quantity": 3, "price": "1.50"},
],
"note": "Ship after 5pm & call first",
}
root = ET.Element("order", {"id": order["id"], "currency": order["currency"]})
customer = ET.SubElement(root, "customer")
ET.SubElement(customer, "name").text = order["customer"]["name"]
ET.SubElement(customer, "email").text = order["customer"]["email"]
items = ET.SubElement(root, "items")
for line in order["items"]:
item = ET.SubElement(items, "item", {"sku": line["sku"]})
ET.SubElement(item, "quantity").text = str(line["quantity"])
ET.SubElement(item, "price").text = line["price"]
ET.SubElement(root, "note").text = order["note"]
xml = ET.tostring(root, encoding="unicode")
print(xml)
ElementTree escapes text during serialization, so the note becomes safe XML. When parsing XML from untrusted sources on the way back to JSON, follow Python's XML security guidance; entity expansion and external references belong in your threat model.
Convert JSON to XML Online
Use the JSON to XML converter when you need a quick browser-local conversion:
- Paste or upload JSON.
- Repair and validate it first if the input came from a log, LLM, issue comment, or copied API response.
- Convert to XML.
- Check the root element, repeated element names, attributes, and escaped text.
- Copy the XML or send it to the XML formatter if you want indentation changes.
For sensitive data, still treat the browser like a work surface: redact tokens, customer records, private keys, and credentials before pasting them into any site, screenshot, ticket, or chat.
When Not to Convert JSON to XML
Do not convert just because XML is available. Keep JSON when both systems support it and there is no schema, SOAP envelope, legacy import, publishing feed, sitemap, or document workflow requiring XML. Converting adds decisions, and each decision is a chance to lose type information or change shape.
Convert when a receiving system requires XML, when a standard is XML-native, or when you need to feed a validator that only accepts XSD-backed documents. Otherwise, JSON is usually the simpler interchange format.
Frequently Asked Questions
How do I convert JSON to XML?
Choose one root element, turn object keys into child elements, map @ keys to attributes, put text for attribute-bearing elements under #text, repeat elements for arrays, sanitize invalid XML names, and escape XML-sensitive characters.
What root element should I use?
If the JSON has one top-level key, use that key. If it has multiple keys or starts with an array, choose a domain wrapper such as <order>, <users>, or <payload> instead of relying on a generic <root>.
How does a JSON array become XML?
A JSON array usually becomes repeated sibling elements with the same tag name, such as <item>...</item><item>...</item>. A top-level array still needs a wrapper element around those repeated children.
How do I make a JSON value an XML attribute?
Use an @-prefixed key such as @id or @currency. If the same element also has text, store the text under #text, for example { "price": { "@currency": "USD", "#text": "9.99" } }.
How do I preserve text and attributes together?
Represent the element as an object: attributes use @ keys and text uses #text. Without that convention, a converter has no reliable place to put both the attribute data and the element text in JSON.
What if a JSON key is not a valid XML element name?
Rename or sanitize it before conversion. Keys with spaces, leading digits, or punctuation should become XML-safe names such as shipTo, _1stPlace, or orderTotal; colons should be reserved for real namespace prefixes.
Does JSON to XML preserve numbers, booleans, and nulls?
Not exactly. XML stores text, so numbers and booleans come back as strings unless the parser or schema re-infers types. null needs a documented policy: empty element, omitted element, or an explicit nil attribute.
Is online JSON to XML conversion safe for sensitive data?
Use a browser-local converter for ordinary payloads, but redact secrets before pasting data anywhere. Tokens, private keys, customer records, and internal logs should be handled with the same care you would use for screenshots or support tickets.
Related Tools & Guides
- JSON to XML Converter - convert JSON into XML in your browser
- XML Formatter - beautify and re-indent the XML output
- XML to JSON Conversion: Attributes, Text Nodes, Arrays, and Namespaces - the reverse direction
- How to Convert CSV and XML to JSON
- How to Convert JSON to CSV - the same JSON, a different target format
- What Is JSON? - the source format's types and rules
Sources
- W3C XML 1.0 Specification - root element, names, attributes, entities, and well-formedness rules
- fast-xml-parser - JavaScript
XMLBuilder - xmltodict - Python
unparse(),@attributes, and#text - Python ElementTree documentation - standard-library XML construction and serialization
Last reviewed July 2026.