JSON and XML don't share a data model, so any converter between them is really committing to a convention and hoping it matches what you need. This one's convention is small and explicit: a key starting with @ becomes an attribute, a key of #text becomes text content, and an array becomes repeated sibling elements under that key's tag name — not a wrapper element with numbered children, which is the other common choice and reads worse in practice.
The example below is loaded into the converter above. Two orders, both with an @id attribute, become two sibling <order> elements rather than one <orders><order>...</order><order>...</order></orders> wrapper — the array key itself (order) is already the repeated tag name.
Before and after
This is loaded in the converter above.
{
"orders": {
"order": [
{
"@id": "1001",
"customer": "Ana Torres",
"total": "129.90",
"status": "shipped"
},
{
"@id": "1002",
"customer": "Bruno Lima",
"total": "44.00",
"status": "pending"
}
]
}
}<?xml version="1.0" encoding="UTF-8"?>
<orders>
<order id="1001">
<customer>Ana Torres</customer>
<total>129.90</total>
<status>shipped</status>
</order>
<order id="1002">
<customer>Bruno Lima</customer>
<total>44.00</total>
<status>pending</status>
</order>
</orders>
How the mapping works
@key becomes an attribute
A key that starts with @ is stripped of that prefix and attached to its element as an attribute, in the order it appears in the object. @id: "1001" on an order object becomes id="1001" on that <order> tag.
#text becomes text content — usable alongside attributes
An ordinary JSON object has nowhere to put "this element has an attribute and also text" — objects don't have an implicit position for text the way XML elements do. #text is the explicit key for that: {"@id": "1", "#text": "hello"} produces <tag id="1">hello</tag>.
Arrays become repeated elements, not a wrapper
order: [ {...}, {...} ] produces two sibling <order> elements — the key itself supplies the repeated tag name. A bare array with no such key (converting a JSON array directly, with nothing wrapping it) has no natural tag name to reuse, so it falls back to a generic <item> for each entry inside a default <root>.
null becomes an empty element; every other scalar becomes text
A JSON null has no XML equivalent, so it becomes a self-closing empty element: null -> <key/>. Numbers and booleans become their string form as element text — 8080 becomes the text "8080" — since XML text is always just characters.
Known limitations
- A JSON key that is not a valid XML name — spaces, a leading digit, most punctuation — is rewritten rather than rejected: invalid characters become _, and a name that would still start with a digit gets a leading _. This is a visible, tested transformation (2fa becomes _2fa), not a silent one, but it does mean the output tag name is not always identical to the input key.
- Converting a JSON array back from XML and forth again isn't perfectly stable when the array holds a mix of objects and plain values — the mapping is designed around arrays of one consistent shape, which covers the overwhelming majority of real API responses and config arrays.
JSON to XML FAQ
Why @ for attributes instead of some other convention?
There's no standard here — several JSON-XML libraries use @, and it has the advantage of sorting distinctly from ordinary keys and being unambiguous inside a plain-text JSON key. This converter documents its exact choice rather than assuming it's the only reasonable one.
Can I convert a JSON array directly, with nothing wrapping it?
Yes — it gets wrapped in a default <root>, with each item as an <item> element, since a bare array has no key of its own to reuse as the repeated tag name.
Is the conversion reversible?
For the shapes this convention targets — objects, attributes, arrays of one consistent shape — yes: converting the result back with XML to JSON reproduces the same JSON, checked by this site's own tests. Mixed text-and-elements content is the one case that isn't: see /xml/to-json's own limitations for why.
Is my JSON uploaded anywhere?
No. The conversion runs in your browser; nothing is sent to a server.
Other conversions
Every direction between JSON, XML and YAML — including YAML as a starting point — is available directly from the From/To selectors in the converter above, whichever page you're on.