Developer10 min readUpdated 2026-08-05

How to Convert XML to JSON (And Back) Without Losing Data

XML and JSON hold the same kind of information in very different shapes. XML wraps everything in opening and closing tags and can hang extra data off a tag as attributes. JSON uses keys, braces and square brackets, and has no concept of an attribute at all. Converting between them is mostly mechanical — until you hit the places where the two formats genuinely disagree.

For a straightforward file the job takes about five seconds. Paste your XML into the free XML ↔ JSON Converter, press Convert, copy the JSON. It runs entirely inside your browser tab, so an XML export full of customer records or API keys never leaves your machine — nothing is uploaded to a server.

The part worth reading is what happens to the awkward cases: attributes, lists that contain exactly one item, values like 07030 that look numeric but are not, and text mixed in among child tags. Those are the four places a conversion quietly changes your data rather than failing loudly, and they are the reason "it converted fine" and "it converted correctly" are not the same sentence.

XML vs JSON: What Actually Changes

Before converting anything, it helps to know which parts of XML have a clean JSON equivalent and which do not.

XML featureJSON equivalentClean?
<name>Ana</name>"name": "Ana"Yes
Nested tagsNested objectsYes
Repeated sibling tagsArrayOnly if repeated 2+ times
Attributes id="42"No equivalent — needs a naming conventionNo
Text mixed with child tagsNo equivalentNo
Comments, DOCTYPE, processing instructionsNone — JSON has no commentsNo
Namespaces ns:itemKey becomes the literal string "ns:item"Workable

The pattern is clear enough: XML can express things JSON cannot. Converting XML to JSON is therefore a lossy operation by default, and converting the JSON back to XML will not reproduce the original file byte for byte. If you need a perfect round trip, keep the original XML.

The reverse direction is easier. Almost everything in JSON has an obvious XML shape — objects become nested elements, arrays become repeated tags. The only real problem is that JSON allows things XML forbids in a tag name, which is covered further down.

How to Convert XML to JSON (Step by Step)

  1. Open the converter. Go to the XML ↔ JSON Converter. Nothing to install, no account needed.
  2. Make sure the mode is "XML to JSON". There are two buttons at the top. The left one, XML to JSON, is selected by default. Switching modes clears the output box, so pick the direction before you paste.
  3. Paste your XML into the left box. This is a paste-in tool, not an upload tool — there is no file picker. If your XML is in a .xml file, open it in a text editor (or drag it into a browser tab), select all, and paste. That is also why nothing touches a server: the text never leaves the page.
  4. Press Convert. The button sits under both boxes. Conversion is not live as you type — you press it once, and again after any edit.
  5. Copy the JSON. Use the Copy link above the right-hand box. It confirms with a "Copied" tick for two seconds. There is no download button, so paste it straight into your editor and save from there.

Errors appear as a red bar under the boxes reading "Invalid XML input." Note the important limitation described in the trap section below: this message fires far less often than you would expect, because the parser is deliberately forgiving.

If the JSON that comes out is a wall of unindented text after you have pasted it somewhere else, run it through the JSON Formatter to re-indent and validate it, or the JSON Viewer to browse it as a collapsible tree.

What the Conversion Does to Your Data

Here is exactly how the converter maps XML onto JSON. Every example below is real input and real output from the tool.

Nested tags become nested objects

<?xml version="1.0" encoding="UTF-8"?>
<note>
  <to>Ana</to>
  <body><p>hi</p></body>
</note>
{
  "?xml": "",
  "note": {
    "to": "Ana",
    "body": { "p": "hi" }
  }
}

Straightforward, with one piece of debris: the XML declaration line becomes an empty "?xml" key. It is harmless but meaningless — delete it before using the JSON anywhere real.

Repeated tags become an array

<catalog><book>A</book><book>B</book><book>C</book></catalog>

{ "catalog": { "book": [ "A", "B", "C" ] } }

Numbers and booleans are converted, not quoted

<r><qty>25</qty><price>19.99</price><ok>true</ok></r>

{ "r": { "qty": 25, "price": 19.99, "ok": true } }

XML has no types — everything in an XML document is text. The converter guesses, and for genuine numbers and true/false the guess is what you want. For values that only look numeric, it is not; see Trap 3.

Empty and self-closing tags become empty strings

<r><a></a><b/><c>  spaced  </c></r>

{ "r": { "a": "", "b": "", "c": "spaced" } }

Note the third one: surrounding whitespace is trimmed off every value. If your XML holds text where leading spaces are significant — code snippets, fixed-width records, poetry — they are gone.

Entities are decoded, CDATA is unwrapped

<r><t>Tom &amp; Jerry</t><u>5 &lt; 10</u></r>

{ "r": { "t": "Tom & Jerry", "u": "5 < 10" } }

This is correct behaviour. &amp; is XML's way of writing a literal ampersand, and JSON needs no such escape, so it becomes a plain &. Content inside <![CDATA[ ... ]]> is likewise unwrapped to its raw text.

Namespace prefixes stay in the key name

<ns:root xmlns:ns="http://x"><ns:item>1</ns:item></ns:root>

{ "ns:root": { "ns:item": 1 } }

The colon is legal in a JSON key, so this works — but remember that data["ns:item"] is the only way to read it in most languages. Dot access will not.

The Five Traps That Silently Corrupt Data

None of these throw an error. Each one produces JSON that looks fine and is wrong. This is the section to read twice.

Trap 1 — Attributes are dropped

This is the big one. XML attributes have no JSON equivalent, and rather than invent one, the converter discards them:

<book id="42" lang="en"><title>Dune</title></book>

{ "book": { "title": "Dune" } }

The id and lang are gone, with no warning. If your XML carries meaningful data in attributes — and a great deal of real-world XML does, especially API responses, RSS feeds, SVG and configuration files — check the output for it before you rely on the result.

The workaround: promote the attributes into child elements before pasting. Rewrite <book id="42"> as <book><id>42</id>, and the value survives as "id": 42. For a handful of records, do it by hand in your editor. For a large export, a find-and-replace with a regular expression will do it in one pass — the Regex Tester is a good place to get the pattern right before you run it on the real file.

Trap 2 — A list of one is not an array

This one breaks production code more often than any other conversion bug. Compare:

<catalog><book>A</book><book>B</book></catalog>
→  { "catalog": { "book": [ "A", "B" ] } }        ← array

<catalog><book>A</book></catalog>
→  { "catalog": { "book": "A" } }                 ← string, not an array

XML cannot tell the difference between "a list that happens to contain one book" and "a single book" — both are written identically. So the converter guesses from what it sees, and the shape of your JSON changes depending on how many records the file happened to contain.

The consequence: code written against a two-record test file, doing data.catalog.book.forEach(...), crashes the day a customer uploads a one-record file. Always normalise on the way in — in JavaScript, const books = [].concat(data.catalog.book ?? []) gives you an array in every case.

Trap 3 — Leading zeros are destroyed

Because the converter turns numeric-looking text into real numbers, anything that is formatted as digits but is not a quantity gets mangled:

<r><zip>07030</zip><phone>+15551234</phone></r>

{ "r": { "zip": 7030, "phone": 15551234 } }

A New Jersey ZIP code became a four-digit number. The + on the phone number vanished. The same applies to product SKUs, account numbers, invoice references, ISBNs, German postcodes and anything else where a leading zero carries meaning. It is the identical failure mode that mangles ZIP codes when you open a CSV in Excel, and it is just as silent.

Check any ID-like field in the output before trusting it. If a value should be text, re-quote it manually, or handle it downstream as a string.

Trap 4 — Mixed content loses its spacing

When a tag contains both text and child tags, JSON has nowhere sensible to put it. The text fragments get collected into a #text key — and joined without their separating spaces:

<p>Hello <b>world</b> now</p>

{ "p": { "b": "world", "#text": "Hellonow" } }

"Hello" and "now" have been concatenated into Hellonow, and the original word order is unrecoverable from the JSON. If your XML is document-flavoured — XHTML, DocBook, formatted descriptions with inline <em> or <a> tags — do not convert it to JSON at all. Store the fragment as an escaped XML string instead and parse it where you render it.

Trap 5 — Broken XML often does not error

The parser is forgiving by design, so malformed XML frequently converts without complaint:

<a><b>unclosed</a>          →  { "a": { "b": "unclosed" } }
hello world                  →  { }
(empty input)                →  { }

The mismatched closing tag in the first line is a genuine XML error, and it still produces output. Plain text produces an empty object rather than an error. So an empty {} is the real failure signal here — if you press Convert and get {} or {"?xml": ""} back, your input was not valid XML, whatever the absence of a red error bar suggests. Empty output is a result you should never accept at face value.

How to Convert JSON to XML

The reverse direction uses the same page — click JSON to XML at the top, paste JSON on the left, press Convert. Output is indented automatically.

{ "catalog": { "book": [ { "title": "A" }, { "title": "B" } ] } }
<catalog>
  <book>
    <title>A</title>
  </book>
  <book>
    <title>B</title>
  </book>
</catalog>

Arrays become repeated sibling tags, which is exactly right. Characters that XML requires you to escape are escaped correctly — Tom & Jerry comes out as Tom &amp; Jerry, 5 < 10 as 5 &lt; 10 — so you can paste text values in without pre-processing them.

Four things to know before you use the output:

  1. Your JSON needs exactly one top-level key. XML requires a single root element. Feed in {"a":1,"b":2} and you get <a>1</a><b>2</b> — two roots, which is not valid XML. Wrap your data in one outer key first: {"root":{"a":1,"b":2}}.
  2. A top-level array produces numeric tag names. [{"a":1},{"a":2}] becomes <0>...</0><1>...</1>. Tag names cannot begin with a digit, so this output is invalid XML and most parsers will reject it. Wrap the array in a named key instead: {"items":[{"a":1},{"a":2}]}.
  3. Keys must be legal XML names. {"my key":"v"} produces <my key>v</my key>, which will not parse — spaces are not allowed in tag names. Rename such keys to my_key before converting. The same applies to keys starting with a digit.
  4. Empty arrays disappear and there is no XML declaration. A key whose value is [] is dropped entirely from the output rather than producing an empty tag, and null becomes a self-closing <key/>. The output also has no <?xml version="1.0"?> line — add it yourself if the consuming system expects one.

If your JSON does not parse, the tool shows "Invalid JSON input." Unlike the XML direction, this check is strict and reliable — a trailing comma or single-quoted key will be caught. Run questionable JSON through the JSON Formatter first to see exactly where the syntax breaks.

Converting XML to JSON in Code

For a one-off file, the browser tool is faster than writing anything. For a recurring job, here is the equivalent in the four environments people ask about most. All of them keep attributes if you configure them to, which is the main advantage over a quick paste-and-convert.

JavaScript / Node.js

npm install fast-xml-parser

import { XMLParser } from 'fast-xml-parser';

const parser = new XMLParser({
  ignoreAttributes: false,      // keep attributes
  attributeNamePrefix: '@_',    // as "@_id", "@_lang"
  parseTagValue: false,         // keep "07030" a string
});
const json = parser.parse(xmlString);

Those three options turn off the three traps above. ignoreAttributes: false preserves attributes, and parseTagValue: false leaves every value as text so leading zeros survive.

Python

pip install xmltodict

import xmltodict, json

with open('data.xml') as f:
    data = xmltodict.parse(f.read())     # attributes kept as "@id"
print(json.dumps(data, indent=2))

xmltodict keeps attributes by default with an @ prefix and never coerces types — every value stays a string. For bulk conversions where correctness matters more than convenience, this is the safest of the four.

PHP

$xml  = simplexml_load_file('data.xml');
$json = json_encode($xml, JSON_PRETTY_PRINT);

Short, but be aware it moves attributes into an @attributes sub-object and has the same one-item-is-not-an-array behaviour described in Trap 2.

Command line

brew install yq          # or: apt install yq
yq -p=xml -o=json data.xml > data.json

Useful in a shell pipeline or a cron job. yq keeps attributes with a +@ prefix by default.

Tips

  • Convert a small sample first. Take twenty representative records, convert them, and read the JSON by eye against the XML. Ten minutes here catches every trap in this guide before it reaches a 200MB export.
  • Search the output for the values you care about. Faster than reading the whole thing: pick three or four important attribute values and IDs from your XML and Ctrl+F for them in the JSON. Anything missing tells you immediately what got dropped.
  • Watch for numbers with leading zeros. ZIPs, SKUs, invoice numbers, account references, ISBNs. If a field is an identifier rather than a quantity, it should be a string.
  • Delete the ?xml key. It carries nothing and it will confuse anything that iterates over top-level keys.
  • Normalise arrays on the way in, not everywhere downstream. One helper that coerces single objects into single-item arrays beats defensive checks scattered through your codebase.
  • Keep the original XML. Converting back does not reproduce it. Attributes, comments and the declaration are gone for good.
  • Format after converting. Paste the result into the JSON Formatter to validate and pretty-print, or the JSON Viewer to explore a large structure as a collapsible tree.
  • Going further than JSON? The same data can go on to a spreadsheet with the JSON to CSV converter, or to config-friendly YAML with the YAML ↔ JSON Converter.

FAQs

Is converting XML to JSON lossless?
No, and it cannot be. XML can express things JSON has no way to represent — attributes, comments, processing instructions, the distinction between a one-item list and a single value, and text mixed in among child elements. A simple data-only XML file converts cleanly. A document-flavoured or attribute-heavy one loses information. Always keep the original XML file.
What happens to XML attributes when I convert to JSON?
There is no attribute concept in JSON, so every converter has to choose: drop them, or rename them into ordinary keys using a prefix such as @_id. FileNaut's browser converter currently drops them, so <book id="42"><title>Dune</title></book> becomes {"book":{"title":"Dune"}}. If your attributes hold real data, promote them to child elements in your XML first — <book><id>42</id> — or use a library-based conversion with ignoreAttributes: false.
Why did my ZIP code 07030 turn into 7030?
XML has no data types — everything in the file is text — so the converter guesses which values are numbers and converts them. That is what you want for <qty>25</qty> and wrong for anything that only looks numeric. Leading zeros disappear, and a leading + on a phone number is stripped. Check every ID-style field after converting, and re-quote it as a string if the formatting matters.
Why is my array a plain string when there is only one record?
XML writes "one book" and "a list of one book" identically, so a converter cannot tell them apart and returns a single value rather than a one-item array. The JSON shape therefore changes with the number of records in the file. Normalise on the way in — in JavaScript, [].concat(data.catalog.book ?? []) always yields an array. This is the single most common cause of code that works in testing and crashes in production.
Can I convert a large XML file, and is my data uploaded anywhere?
Nothing is uploaded — the conversion runs in JavaScript inside your own browser tab, so your XML never reaches a server. That makes it safe for customer exports, invoices and API responses containing keys. The practical limit is what your browser will accept in a textarea: the tool takes pasted text rather than a file, so very large exports (tens of megabytes) are better handled with a script. See the code examples above.
I converted JSON to XML and the result will not parse. Why?
Almost always one of three causes. Your JSON had more than one top-level key, producing multiple root elements — XML permits exactly one. Or it was a top-level array, producing numeric tag names like <0>, which are illegal. Or a key contained a space or started with a digit, which XML forbids in a tag name. Wrap your data in a single named root key and rename any offending keys before converting.
The converter returned an empty {} but showed no error. What went wrong?
The XML parser is deliberately forgiving, so it rarely refuses input outright. Plain text, an empty box, or content that is not XML at all returns an empty object instead of an error. Treat {} — or an output containing only "?xml": "" — as the failure signal. Check that you pasted the whole document including its root element, and that the mode toggle is set to XML to JSON rather than JSON to XML.
Should I convert my XML API responses to JSON permanently?
For working with data in JavaScript, Python or a modern API layer, yes — JSON is lighter, natively parsed by every language, and far easier to read. But convert at the boundary and keep the XML as the archived original. XML still wins where you need schema validation (XSD), digital signatures, or genuine mixed-content documents. Converting a signed or schema-validated XML document to JSON throws away exactly the properties it was chosen for.
Is the XML to JSON converter free?
Yes. The XML ↔ JSON Converter is free with no signup, no watermark and no file limit imposed by us. Because the work happens in your browser rather than on a server, there is no per-conversion cost to pass on. The same applies to the JSON Formatter, YAML ↔ JSON Converter and CSV to JSON tools.

Ready to try it?

Use the tool right now — free, no signup, no upload.