Convert XML to JSON, attributes and text nodes included. Attributes become keys with an underscore prefix (_id), and text that sits next to child elements goes under __text. Use it when a SOAP response or an RSS feed has to reach code that only reads JSON.
<root>
<person>
<id>1</id>
<name>John Doe</name>
<role>Admin</role>
</person>
<person>
<id>2</id>
<name>Jane Smith</name>
<role>User</role>
</person>
</root> {
"root": {
"person": [
{
"id": "1",
"name": "John Doe",
"role": "Admin"
},
{
"id": "2",
"name": "Jane Smith",
"role": "User"
}
]
}
} Normalize repeated elements before the JSON reaches a frontend. One <order> becomes an object and two become an array, so the same SOAP call can return two shapes.
RSS and Atom are XML, while most code that consumes them today works with JSON.
A deeply nested XML config is easier to scan as JSON when you only want to see what is in it.
<?php
$xml = '<catalog>
<book id="1" lang="en"><title>Dune</title><price>9.99</price></book>
<book id="2"><title>Emma</title><price/></book>
</catalog>';
$obj = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
if ($obj === false) {
die('Malformed XML');
}
echo json_encode($obj, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); Attributes land under @attributes and an empty element becomes {} rather than "" or null. LIBXML_NOCDATA turns CDATA sections into plain strings.
import json
import xml.etree.ElementTree as ET
xml = """<catalog>
<book id="1" lang="en"><title>Dune</title><price>9.99</price></book>
<book id="2"><title>Emma</title><price/></book>
</catalog>"""
def to_dict(el):
node = {"@" + k: v for k, v in el.attrib.items()}
for child in el:
value = to_dict(child)
if child.tag in node: # repeated tag -> list
if not isinstance(node[child.tag], list):
node[child.tag] = [node[child.tag]]
node[child.tag].append(value)
else:
node[child.tag] = value
text = (el.text or "").strip()
if text and not node:
return text
if text:
node["#text"] = text
return node or None
root = ET.fromstring(xml)
print(json.dumps({root.tag: to_dict(root)}, indent=2)) Standard library only. If you would rather not maintain this, xmltodict.parse(xml) from the xmltodict package does the same job and uses the same @ and #text keys.
// npm install fast-xml-parser
const { XMLParser } = require("fast-xml-parser");
const xml = '<catalog><book id="1"><title>Dune</title><price>9.99</price></book>' +
'<book id="2"><title>Emma</title><price/></book></catalog>';
const parser = new XMLParser({
ignoreAttributes: false,
attributeNamePrefix: "@_",
isArray: (name) => name === "book", // always a list, even with one <book>
});
console.log(JSON.stringify(parser.parse(xml), null, 2)); Without the isArray option a parent with one child gives an object and a parent with two gives an array, which is the kind of bug that only appears with real data.
This tool runs on x2js, which puts attributes under underscore-prefixed keys (_id) and mixed text under __text. Every library picks its own scheme: SimpleXML uses @attributes, xmltodict uses @id and #text, fast-xml-parser defaults to @_id. Pick one and agree on it before the JSON goes anywhere else.
Comments, processing instructions and the DOCTYPE are dropped. JSON has nowhere to put them. If you need a lossless round trip, keep the XML as the source of truth and treat the JSON as a view of it.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
Attributes become keys with an underscore prefix, so <item id="5"> gives "_id": "5". When an element has attributes or child elements as well as text, the text goes under a __text key. Nothing in the element is lost.
Elements that occur more than once under the same parent are collected into a JSON array. A single occurrence becomes a plain object or string.
Partly. Keys use the element's local name, so soap:Envelope becomes Envelope, and the prefix is kept in a __prefix field inside it. Namespace declarations such as xmlns:soap appear as attributes (_xmlns:soap).
The input must be well-formed: every tag closed, attributes quoted and a single root element. The browser's XML parser rejects anything else, and the tool then shows Invalid XML without a position, so check for an unclosed tag or a bare & first.
Yes. Paste the raw XML, or fetch it with Load URL when the server allows cross-origin requests. RSS, Atom, SOAP, SVG and sitemap files are all regular XML.
JSON to XML converts in the other direction. Once you have JSON, the formatter tidies it, and JSON to YAML converts it again if the destination is a config file.