What the conversion actually does
XML and JSON model data differently, so no conversion is entirely lossless. XML has attributes, ordered children, namespaces, comments and mixed content; JSON has objects, arrays and four scalar types. Every converter has to pick a set of conventions, and the useful thing is knowing which ones are in play.
The rules used here
- The root element becomes the single top-level key, so the document keeps its name.
- Attributes become keys prefixed with
@. - An element with no attributes and no children becomes its trimmed text content.
- Two or more siblings sharing a tag name collapse into an array.
- Mixed text alongside child elements is stored under
#text. - CDATA sections are read as ordinary text.
- Values stay strings — nothing is coerced to a number or boolean.
Example
<!-- input -->
<order id="A-1093">
<item sku="TEA-01">2</item>
<item sku="MUG-07">1</item>
</order>
// output
{
"order": {
"@id": "A-1093",
"item": [
{ "@sku": "TEA-01", "#text": "2" },
{ "@sku": "MUG-07", "#text": "1" }
]
}
}When the parse fails
XML is far stricter than HTML. Every tag must close, attribute values must be quoted, and the five reserved characters — &, <, >," and ' — must be written as entities inside text. A bare ampersand in a URL is the single most common cause of a failed parse. Unclosed tags and a stray byte-order mark before the declaration come next.
When the document is not well-formed, the browser's parser error is reported directly rather than being swallowed, so you can see which construct it objected to.
Doing this in your own code
Most languages need a library, because none of them ship an XML-to-JSON bridge in the standard library:
- JavaScript / Node —
fast-xml-parserorxml2js. In the browser,DOMParserplus a short walk (what this page does) avoids a dependency. - Python —
xmltodict.parse(xml)returns a dict thatjson.dumpsserializes directly. - Java —
org.json.XML.toJSONObject(xmlString)is a one-liner; Jackson'sXmlMapperhandles larger jobs. - C# —
JsonConvert.SerializeXNodein Newtonsoft.Json.
Each library picks slightly different conventions for attributes and single-element arrays, so compare their output against what your consumer expects before committing. Once you have JSON, the formatter and validator take it from there.
What the conversion cannot carry across
Three things have no JSON equivalent and are dropped or flattened. Namespacessurvive only as part of the tag name — soap:Envelope stays a literal key, so prefixes that vary between responses will produce keys that vary with them. Comments and processing instructions are discarded, including the XML declaration. Anddocument order between different tag names is preserved only by accident: JSON object keys are conventionally unordered, so a consumer is entitled to reorder them.
That last point is the one that causes real bugs. If your XML is a sequence where order carries meaning — a list of steps, a transaction log — and the entries have different tag names, the JSON representation cannot express that ordering. The fix is to restructure into an array before the order is lost, which usually means changing the XML rather than the converter.
Where XML still shows up
Plenty of places, despite JSON having won the API argument: SOAP services in finance and logistics, RSS and Atom feeds, SAML assertions, Office and OpenDocument file formats, Android layouts, Maven builds and sitemaps. Most of the time you are not choosing XML — you are consuming something that already made the choice years ago, and converting it is the shortest path to code that is pleasant to write.