XML to JSON Converter

Paste XML and get predictable JSON: attributes as @name, repeated elements as arrays, with clear parse errors.

Overview

XML is still everywhere older integrations live: SOAP services, bank and airline feeds, RSS and Atom, Maven builds, Android resources and countless configuration files. Most code written today would rather consume JSON, so sooner or later you need to convert one to the other to inspect a payload or feed a script.

This converter parses your XML with the browser's own XML parser, so a document that is not well-formed is rejected with the parser's line and column instead of producing half a result. The tree is then mapped to JSON with a small, fixed set of rules that are spelled out below. Everything runs in your browser and nothing is uploaded; your last input is kept in local storage so it survives a page refresh.

Why there is no single "correct" XML to JSON mapping

XML and JSON describe data with different building blocks, so every converter has to make choices, and two tools will often disagree on the same input. The main mismatches are:

  • Attributes versus elements. <price currency="USD"> carries data in two places. JSON has only keys, so attributes need a marker that keeps them apart from child elements with the same name.
  • Order. XML element order is significant; JSON object keys are officially unordered. Interleaved siblings such as <a/><b/><a/> end up as one a array and one b key, so the original sequence is lost.
  • Mixed content. In <p>Hello <b>world</b>!</p> the text and the element are interleaved. JSON has no natural way to say where the <b> sat inside the sentence.
  • Arrays of one. This is the classic bug. A converter only sees an array when an element repeats, so an order with two <item> children produces "item": [ ... ], but an order with one item produces "item": { ... }. Code that does order.item.forEach(...) works in testing and then fails in production on the first single-item order.

The fix for the last one is to tell the converter which elements are lists. Type the tag names into Force arrays, separated by commas, and they are always emitted as arrays, even with one child. If you consume the JSON in code, do this for every element your schema allows to repeat.

The conversion rules this tool uses

XMLJSON
<name>Ada</name>"name": "Ada"
<price currency="USD">9.5</price>"price": { "@currency": "USD", "#text": "9.5" }
<tag>a</tag><tag>b</tag>"tag": ["a", "b"]
<empty/> or <x xsi:nil="true"/>"empty": null
<s><![CDATA[a < b]]></s>"s": "a < b"
<!-- note -->dropped, or "#comment": "note" with Keep comments
<?php echo 1 ?>dropped, or "?php": "echo 1" with Keep comments

Put together, a small document converts like this:

<order id="A-1">
  <item sku="KB-01">Keyboard</item>
  <item sku="MS-07">Mouse</item>
  <note>Leave at door</note>
</order>

{
  "order": {
    "@id": "A-1",
    "item": [
      { "@sku": "KB-01", "#text": "Keyboard" },
      { "@sku": "MS-07", "#text": "Mouse" }
    ],
    "note": "Leave at door"
  }
}

Text inside an element that also has attributes or children goes under #text. With mixed content, the text pieces are joined with a space and their position relative to the child elements is not preserved. If you change the attribute prefix to _ or $, pick the same prefix in the JSON to XML converter and the output converts back to equivalent XML.

Why numbers and booleans stay strings by default

XML has no types without a schema: <zip>02134</zip> and <count>42</count> are both just text. Guessing types breaks real data. ZIP codes, account numbers and ISBNs lose their leading zeros, version strings like 1.10 become 1.1, and a 19-digit order ID silently changes because JavaScript numbers only hold integers exactly up to 253.

When you switch on Numbers & booleans, the converter only changes a value if nothing is lost: 42, -3.25, true and false are converted, while 007, 1.50, 1e3 and 9007199254740993 stay strings. It is a convenience for reading data, not a substitute for a schema.

Common uses and gotchas

  • SOAP and legacy APIs. Paste a SOAP envelope to see the response as a tree you can query. Untick Keep namespace prefixes to turn soap:Envelope and ns2:getQuoteResponse into plain Envelope and getQuoteResponse; the xmlns declarations are dropped at the same time. Only do this when local names are unique, because two namespaces can use the same local name.
  • RSS and Atom feeds. Add item (RSS) or entry (Atom) to Force arrays so a feed with one post has the same shape as a feed with fifty.
  • Maven and Android files. pom.xml dependencies and Android strings.xml resources become easy to scan or diff once they are JSON. Use the diff checker to compare two converted versions.
  • Entities. XML only predefines &amp;, &lt;, &gt;, &quot; and &apos;. HTML entities such as &nbsp; fail with "Entity 'nbsp' not defined" unless the document declares them; replace them with numeric references like &#160;.
  • Whitespace. With Trim text on, leading and trailing whitespace is removed from every text value. Turn it off when spaces are meaningful, for example in fixed-width fields or preformatted text.
  • Common parse errors. "Opening and ending tag mismatch" means a closing tag does not match the open one; "Extra content at the end of the document" means there is more than one root element. To fix indentation or find the broken tag, try the XML formatter.

The parsing rules follow the W3C XML 1.0 specification. Once you have JSON, the JSON viewer is handy for exploring large results.

Frequently Asked Questions

Why is an element an object in one file and an array in another?

Arrays are only created when an element repeats under the same parent. List the tag name under Force arrays and it will always be an array, even when there is just one.

What does the @ in front of some keys mean?

It marks a value that came from an XML attribute rather than a child element. You can change the prefix to an underscore or a dollar sign if @ is awkward in your code.

Is my XML uploaded anywhere?

No. Parsing and conversion happen in your browser with its built-in DOMParser. The input is saved only in your browser's local storage so it is still there after a refresh.

Does the converter fetch DTDs or external entities?

No. The browser parser does not load external DTDs, so entities defined outside the document are reported as undefined. Entities declared in an internal DOCTYPE subset are expanded.