Convert XML to JSON, JSON to XML, and check XML
XML and JSON both describe nested data, but they do it differently. XML has elements with attributes, text, comments, CDATA sections and namespaces. JSON has objects, arrays, strings, numbers, true, false and null. There is no single official mapping between the two, so this XML to JSON converter lets you choose how attributes, repeated elements, namespaces and empty elements come out. The JSON to XML direction uses the same conventions, so a file converted to JSON can be turned back into XML.
Format XML pretty-prints a document with the indent you choose, or puts it on one line, and works as an XML validator: it tells you whether the document is well-formed. The XML is parsed by your browser's own XML parser, which reports errors with a line and a column. For the JSON side, the JSON formatter validates and tidies the result, JSON to CSV turns a list into a spreadsheet, and YAML to JSON converts config files.
How the XML is mapped to JSON
| XML | JSON (default options) |
|---|---|
| <name>Ann</name> | "name": "Ann" |
| <price currency="USD">3.25</price> | "price": { "@currency": "USD", "#text": "3.25" } |
| Two <item> elements in one parent | "item": [ ..., ... ] |
| <note/> | "note": "" |
| <![CDATA[a & b]]> | Text, merged with the element's other text |
| <inv:stock> | "inv:stock" |
| <!-- comment --> | Left out (or "#comment" with Keep comments) |
- Attributes get a prefix, @ by default, so they cannot clash with child elements of the same name. The text of an element that also has attributes or children goes under the text key, #text by default.
- Arrays. When repeated makes an array only for elements that appear more than once in the same parent. Always wraps every element in an array, and Always arrays for does it only for the names you list. Use one of the last two when a program reads the JSON, so a list with one item has the same shape as a list with many.
- Namespaces. The Namespaces in XML recommendation writes a qualified name as prefix:localpart and declares prefixes with xmlns attributes. Keep prefixes keeps both, so the round trip back to XML works. Remove prefixes keeps only the local part and drops the declarations.
- CDATA sections hold text in which < and & need no escaping. According to the XML 1.0 specification, only the closing ]]> is markup inside them. The converter merges their content into the text, or keeps it under #cdata.
- Mixed content, text with elements in between like a paragraph with bold words, has no clean JSON form. The text pieces are joined under the text key and a note tells you that their position between the elements is lost.
- Types. XML has no numbers or booleans, only text. Convert numbers and booleans turns "42" into 42 and "true" into true, but only when the number reads back as the same text, so 6.50, 007 and long IDs stay text.
JSON to XML
A JSON object with exactly one key becomes the root element with that name. Anything else, such as an object with several keys or a top-level array, is wrapped in the root element name you choose. Keys that start with the attribute prefix become attributes, the text key becomes the element's text, #cdata becomes a CDATA section and #comment a comment. Arrays repeat the element once per item. null becomes an empty element like <note/>.
JSON keys can hold anything, but XML names cannot. Under the XML 1.0 rules a name starts with a letter, an underscore or a colon and goes on with letters, digits, hyphens, periods and underscores. A key like "first name" becomes first_name, and "1st" becomes _1st, and a note lists every renamed key. The specification also limits which characters a document may contain at all: most control characters are not allowed, so they are removed and counted. Text is escaped with &, < and >. In attribute values, tabs and line breaks are written as character references, because the XML 1.0 attribute-value normalization would otherwise turn them into spaces.
Validate and format XML
XML is strict. A document is well-formed only when every element is closed in the right order, attribute values are quoted, there is exactly one root element and every & starts a known entity. The browser's parser checks this, and MDN describes that for malformed XML it returns a document with a parsererror node instead of throwing an error. The tool reads the line, the column and the message from that node, adds a plain explanation for common mistakes and offers a Jump to error button. The wording of the message differs between browsers.
Formatting only changes whitespace between elements. An element that holds text, whether plain text, CDATA or text mixed with child elements, is written exactly as it was. The same goes for an element with xml:space="preserve", which the XML 1.0 specification defines as a signal that applications should keep all whitespace. The XML declaration, the DOCTYPE with its internal subset, comments and processing instructions stay in place.
Worked example
This bakery file has a namespace, attributes, two products and a CDATA section:
<bakery xmlns:inv="https://example.com/inventory">
<name>Corner Bakery</name>
<product id="101" vegan="true">
<title>Sourdough loaf</title>
<price currency="USD">6.50</price>
<inv:stock>24</inv:stock>
</product>
<product id="102">
<title>Croissant</title>
<price currency="USD">3.25</price>
<inv:stock>0</inv:stock>
<note><![CDATA[Contains butter & milk]]></note>
</product>
</bakery> With the default options, the converter finds 11 elements and 6 attributes (the xmlns declaration counts as one). Because product appears twice, it becomes an array, while name stays a single string:
{
"bakery": {
"@xmlns:inv": "https://example.com/inventory",
"name": "Corner Bakery",
"product": [
{
"@id": "101",
"@vegan": "true",
"title": "Sourdough loaf",
"price": { "@currency": "USD", "#text": "6.50" },
"inv:stock": "24"
},
{
"@id": "102",
"title": "Croissant",
"price": { "@currency": "USD", "#text": "3.25" },
"inv:stock": "0",
"note": "Contains butter & milk"
}
]
}
} Switch on Convert numbers and booleans and set Namespaces to Remove prefixes: "@id" becomes 101, "@vegan" becomes true, the stock keys become "stock": 24 and "stock": 0, and the declaration disappears. The price 3.25 becomes a number, but 6.50 stays text, because as a number it would read 6.5. Converting the default JSON back to XML gives the same document, except that the note is written as escaped text, Contains butter & milk, instead of a CDATA section.
How to use the XML to JSON converter
- 1Choose XML to JSON, JSON to XML or Format XML, then paste your text, drop a file on the box or press Open file.
- 2If an error appears, read the line and column, press Jump to error and fix the spot. The result updates as you type.
- 3Set the attribute prefix, the text key, arrays and namespaces, or the XML format for the other modes.
- 4Press Copy JSON or Copy XML, or download the file. Swap sends the result back as input, so you can check the round trip.
Sources
- W3C: Extensible Markup Language (XML) 1.0, Fifth Edition (names, characters, CDATA, attribute-value normalization, xml:space)
- W3C: Namespaces in XML 1.0 (qualified names, xmlns declarations)
- MDN: DOMParser.parseFromString() and the parsererror node
Frequently asked questions
Is my XML uploaded anywhere?
No. The XML is parsed by your own browser and converted on your device. Nothing is sent to a server, and Copy link only shares your settings, never the text.
Why is one element an array and another not?
With Arrays set to When repeated, an element becomes a JSON array only when it appears more than once in the same parent. That keeps simple files readable, but a list with one item then looks different from a list with two. Set Arrays to Always, or list the element names under Always arrays for, to get the same shape every time.
What happens to attributes?
Each attribute becomes a key with the attribute prefix in front, @ by default, so <price currency="USD"> gives "@currency": "USD". The element’s text then goes under the text key, #text by default. With an empty prefix, attributes sit next to child elements, and a name used by both is put in an array.
Why did 6.50 stay text when I converted numbers?
Convert numbers and booleans only changes a value when the number reads back as exactly the same text. 3.25 becomes 3.25, but 6.50 would turn into 6.5, and 007 into 7, so those stay text and nothing is lost. Very long numbers such as IDs stay text for the same reason.
How do namespaces come out in JSON?
With Namespaces set to Keep prefixes, names keep their prefix, like "soap:Body", and the xmlns declarations become attributes, so you can convert back to the same XML. Remove prefixes gives plain names like "Body" and drops the declarations, which is easier to read but cannot be turned back into the original namespaces.
Does the JSON to XML direction keep long numbers exact?
Yes, in browsers that give the original number text to JSON.parse, which the tool checks for. There, 12345678901234567890 stays 12345678901234567890 in the XML. In other browsers a note warns that numbers are read as 64-bit floating point values and long ones can lose digits.
Why is my XML invalid when it looks fine?
Common causes are a plain & in text (write &), an HTML entity like that XML does not know, a missing end tag, two root elements, or space before the <?xml ...?> declaration. The error shows the line and column the browser reports and a Jump to error button.