Convert XML to JSON
Convert XML to JSON with a configurable attribute prefix, text key and forced-array rules — and an honest note wherever the mapping is lossy.
- Runs in your browser
- Files are never uploaded
- Free, no account
Marks which JSON keys came from XML attributes. Common choices: @, $, _.
Holds an element's text when it also has attributes or children.
Spaces of indentation per level. Enter 0 to indent with tabs.
Comma-separated element names that must be arrays even with one occurrence — the fix for code that breaks when a list has exactly one item.
Your document never leaves this tab — check it yourself—
Starting the network probe…
The counter above wraps fetch, XMLHttpRequest and navigator.sendBeacon in this tab and counts every call. It does not block anything — it just reports.
Two ways to verify independently
- Turn off Wi-Fi or enable airplane mode, then use the tool. It keeps working, because the page is already fully loaded.
- Open your browser devtools, switch to the Network tab, clear it, and run the tool. No new row appears.
Why this matters
In 2025 researchers found that popular online formatters had retained tens of thousands of user-submitted snippets — JSONFormatter.org and CodeBeautify together exposed roughly 80,000 saved documents, many containing live cloud credentials, private keys and internal database connection strings, because “format this” quietly meant “upload this”. A separate incident exposed roughly 3.5 million user PDFs on a document-conversion service. The only structural fix is not sending the data in the first place: this site is served under a Content-Security-Policy whose connect-src contains no third-party origin, so a request off this device cannot be made even by accident.
There is no single correct mapping
XML and JSON are not two encodings of the same data model. XML has attributes, ordered heterogeneous children, mixed content, namespaces, comments and processing instructions. JSON has objects, arrays and four scalar types. Any conversion has to make choices, and every converter makes different ones — which is why the same XML file gives you different JSON on different sites.
This tool makes those choices explicit rather than hiding them, and tells you at the point of conversion when something could not be represented faithfully.
The three decisions you control
Attributes
<item id="1">Widget</item> has two pieces of data with nowhere obvious to
put the first. The convention here prefixes attribute keys, producing
{"item": {"@id": "1", "#text": "Widget"}}. Without a prefix, an
attribute called id and a child element called id would collide and one
would win silently. Change the prefix to $, _ or anything else your
consumer expects.
Single elements versus arrays
This is the bug that breaks production. <items><item/><item/></items>
naturally becomes an array; <items><item/></items> naturally becomes
a single object. Downstream code that does items.item.map(…) works all week and
crashes the day a customer has exactly one order. List the element names in the
always arrays field and they are arrays regardless of how many the document
happens to contain.
Scalar coercion
XML has no types — every attribute and every text node is a string. Turning "42" into
42 is convenient and is wrong for anything that merely looks numeric: postcodes,
phone numbers, version strings, account numbers with leading zeros, ISBNs. Coercion is off by
default for that reason. Turn it on only when you know the shape of your data.
What cannot be represented, and what the tool says about it
| XML feature | In JSON | Consequence |
|---|---|---|
| Mixed content | Text collected into one key | The position of the text relative to sibling elements is lost |
| Order between differently-named siblings | Object keys | Grouped by name; the original interleaving is gone |
| Namespaces | Prefixed names or stripped | Two elements from different namespaces with the same local name collide if you strip |
| Comments | Dropped, or a #comment key | Off by default |
| Processing instructions and DOCTYPE | Dropped | Not representable |
| CDATA | Ordinary text | Correct — CDATA is lexical, not semantic |
Whenever mixed content or interleaved siblings appear in your document, a note is printed under the output naming the problem rather than letting you discover it later.
Namespaces: strip or keep?
Keeping them produces keys like "soap:Envelope" and "dc:title", which are
awkward in JavaScript (you cannot write obj.soap:Envelope) but unambiguous. Stripping
produces "Envelope" and "title", which are pleasant to work with right
up until a document mixes two namespaces that both define title. Strip for a document
you control; keep for anything from an external system.
The reverse direction
The swap button flips the tool into JSON to XML and carries the current output across as the new input, so you can check the round trip. It will not be byte-identical — attribute order, whitespace and any dropped comments see to that — but the data should survive.
Questions
Why does the output have keys starting with @?
Because XML has attributes and JSON does not. The convention used here prefixes attribute names so `<item id="1"/>` becomes `{"item": {"@id": "1"}}` — you can change the prefix to $, _ or anything else. Without a prefix, an attribute and a child element with the same name would collide.
A list with one item comes out as an object instead of an array. How do I fix it?
This is the single most common XML-to-JSON bug, and it is why the "always arrays" field exists. Enter the element names that represent a collection — `item, book, entry` — and they will be arrays even when the document happens to contain exactly one.
What is lost in the conversion?
Three things, and the tool tells you when each applies. Mixed content loses the position of text relative to its sibling elements. Document order between differently-named siblings is lost, because JSON objects group by key. And comments and processing instructions are dropped unless you keep them.
Should I turn on scalar coercion?
Only if you know your data. It turns "42" into 42 and "true" into true, which is convenient — and wrong for anything that merely looks numeric: zip codes, phone numbers, version strings, and IDs with leading zeros. Leading zeros are preserved as strings, but "0000123" is not the only trap.