How XML maps onto JSON
How the XML to JSON converter turns elements, attributes, repeated tags, text and CDATA into JSON, why numbers stay text by default, and how to read its error messages.
Last reviewed · 1,259 words
In short
- The root element becomes the one key of the JSON. Attributes become keys starting with @, and child elements become keys named after their tags.
- A tag that repeats inside the same parent becomes a list. Tick "Make every child element a list" when your program needs the same shape every time.
- Values stay text unless you ask for numbers, because XML has no numbers and "007" or a PIN code must not lose its zeros.
- Mistakes are reported by line and column. A bare & and HTML entities such as are the most common ones.
- A 5.7 MB XML file with 50,000 records converted in about half a second in our test, and nothing is uploaded.
XML is still everywhere: bank and payment gateway responses, SOAP web services, RSS feeds, sitemap files, invoices and tax filings exported from accounting software, configuration files. Modern code and most web tools expect JSON instead. Converting between them is easy for simple files and surprisingly fiddly for real ones, because XML has two things JSON lacks: attributes, and elements that may or may not repeat. This guide explains the rules the converter follows, so the JSON it gives you is predictable.
How to convert XML to JSON
- Paste the XML into the box, or press Open a file and choose an .xml file. RSS feeds, sitemaps, KML and GPX files are XML too.
- Read the JSON on the right. It updates as you type.
- Copy it, or press Download .json.
If the XML has a mistake, the page shows where it is instead of a result. The section on errors below explains the messages.
The rules, element by element
The root element is the top key. XML always has exactly one outermost element. <orders> … </orders> becomes {"orders": { … }}.
Child elements become keys. <city>Pune</city> inside an order becomes "city": "Pune".
Attributes become keys with a prefix. <order id="101"> gives "@id": "101". The prefix keeps an attribute apart from a child element of the same name: an element can have both an id attribute and an <id> child, and JSON needs two different keys for them. Choose _ or - as the prefix if the program reading the JSON expects that convention.
An element with only text is that text. <customer>Asha Rao</customer> becomes "customer": "Asha Rao".
An element with text and attributes keeps its text under #text. <item qty="2">499.75</item> becomes {"@qty": "2", "#text": "499.75"}.
An empty element is an empty string. <note/> and <note></note> both become "note": "".
CDATA is text. <![CDATA[UPI & cash]]> lets XML carry characters such as & and < without escaping them; in the JSON it is plain text, "UPI & cash".
Comments, the XML declaration, processing instructions and the DOCTYPE are left out. They describe the document, not its data.
Repeated elements become lists
This is the rule that causes most surprises. In XML, an order can have one <item> or five, and nothing in the file says which to expect. JSON has to choose between a single value and a list.
The converter does what most others do: a tag that appears more than once inside the same parent becomes a list; a tag that appears once is a single value. In the example on the page, order 101 has two items, so its item is a list of two; order 102 has one, so its item is a single object:
| Order | Items in the XML | item in the JSON |
|---|---|---|
| 101 | 2 | a list: [{"@qty": "2", …}, {"@qty": "1", …}] |
| 102 | 1 | one object: {"@qty": "1", "#text": "2999"} |
That is faithful to the file, but awkward for code that loops over items: it would need to check whether it got a list. Tick "Make every child element a list" and every child becomes a list, even when it appears once. The shape is then the same for every order, at the cost of a few more brackets.
Text, numbers and true or false
Everything in XML is text. <total>2999</total> and <pin>411001</pin> look like numbers, but XML does not say so, and some values that look like numbers are not: a PIN code, a phone number, an account number, an employee code such as 007.
So by default the converter writes every value as a JSON string: "pin": "411001". Nothing is lost or changed.
Tick "Turn numbers and true / false into JSON numbers and booleans" when you know the values are numbers. Then 2999 becomes the number 2999, 499.75 becomes 499.75 and true becomes the boolean true. Even with the option on, values that would change if read as numbers stay text: anything with a leading zero, such as 007, and whole numbers longer than 15 digits, which JavaScript would round.
Spaces and line breaks
XML files are usually indented for reading, so most elements have spaces and line breaks around them that are not data. The converter trims spaces at the start and end of each value, and in an element that mixes text with child elements it joins the text pieces with single spaces: <p>Hello <b>you</b> there</p> becomes {"b": "you", "#text": "Hello there"}. Note that the position of the <b> inside the sentence is not kept; JSON objects have no way to say where a child sat inside text. For documents that are mostly prose with markup inside, such as XHTML pages, JSON is the wrong format.
Line breaks and tabs inside attribute values become spaces, as the XML standard requires.
Reading the error messages
A converter that gives up with "invalid XML" leaves you hunting. This one points to the first mistake by line and column, shows the line, and puts a ^ under the spot. The common ones:
| Message | What it means | Fix |
|---|---|---|
| A bare & is not allowed in XML. | Text such as "Tom & Jerry" | Write &, or wrap the text in CDATA |
| is not an XML entity. | An HTML entity in XML | Type the character, or use a number such as   |
| Expected but found . | Tags closed in the wrong order, or one not closed | Close the inner element first |
| The value of x must be in quotes. | x=1 as in loose HTML | Write x="1" |
| A second root element was found. | Two top-level elements | Wrap them in one parent |
| Text was found before the first element. | Often JSON pasted by mistake | Check what you copied |
XML knows only five named entities: &, <, >, " and '. Everything else must be the character itself or its number, such as ₹ for ₹. Copying text out of a web page is the usual way HTML entities sneak in.
Namespaces
SOAP responses and many government formats use namespaces, which show up as prefixes such as soap:Envelope. The converter keeps names exactly as written, prefix included, and keeps the xmlns declarations as attributes, so <s:Envelope xmlns:s="…"> becomes {"s:Envelope": {"@xmlns:s": "…", …}}. Your code can then read data["s:Envelope"]["s:Body"].
Large files
The page reads XML with its own small parser rather than the browser's, so the error messages are the same in every browser and the reading can be tested. In our test on a laptop, a 5.7 MB XML file with 50,000 customer records, each with an id attribute and four child elements, was converted in about half a second. Files up to 20 MB can be opened; when the JSON is very long, the box shows its start, and Copy and Download carry all of it.
To turn the JSON into a spreadsheet next, use the JSON to CSV or JSON to Excel converter, and to check or pretty-print it, the JSON Formatter.
What this tool assumes
- The input is well-formed XML 1.0: one root element, every element closed, attribute values in quotes, & and < escaped.
- The root becomes the top key; attributes get a prefix (@ by default); repeated child tags become lists; an element with only text becomes that text, and text beside attributes or children goes under #text.
- Comments, processing instructions and the DOCTYPE are dropped. Entities declared inside a DOCTYPE are not supported, and using one is reported as a mistake.
- Values are strings unless you ask for numbers and booleans; leading-zero values and numbers over 15 digits stay strings.
- The XML stays in your browser tab and is not uploaded.