Free, no signup. Change the text in any PDF.Edit a PDF
XML → JSON Converter

Convert XML to JSON - Attributes, Repeated Tags and Text

Upload an XML file and get indented JSON, with the root tag as the top key, @_ attributes and repeated tags as lists.

Select your file to convert

or drop your file here

Expert Tips

What the pros know

Pro Tip

The output uses the same @_ and #text convention as the fast-xml-parser library, so code written against that library's JSON reads our files without changes.

Common Mistake

Looping over a key that is sometimes a list and sometimes a single object. A record with one <item> gives a string or object, not a one-item list. Wrap the value in a list when it isn't one before you loop.

Best For

Product feeds, order exports, SOAP responses and settings files that you want to read in JavaScript or Python, or send to an API that only takes JSON.

Not Recommended

Data you want to open in a spreadsheet. A nested object keyed by the root tag isn't a table; the XML to CSV converter gives you one row per repeated tag instead.

FAQ

Frequently Asked Questions

Upload the file in the box at the top of this page, keep JSON as the output, and download the result. The JSON is one object named after your root tag, with each child tag as a key inside it.

No. Comments, processing instructions and the <?xml ?> header are dropped, because JSON has nowhere to put them. The data inside a CDATA block is kept as an ordinary string.

Your DOCTYPE defines entities, and an entity definition can point at files on our server or expand into gigabytes of text. The converter refuses any file that declares them rather than try to tell safe ones from attacks. Take the entity declarations out, or replace the entities with their values, and upload again. A DOCTYPE with no declarations in it is accepted.

Yes, if the first line declares the encoding, like encoding="Shift_JIS". The converter reads the file in that encoding and writes the JSON as UTF-8. A file in another encoding with no declaration is refused, because there is no safe way to guess it.

20 MB per file, and up to 5 files at once. Inside that, a file can have up to 2,000,000 elements and 64 levels of nesting. Bigger files are stopped with a message asking you to split them.

Yes. Your file is uploaded and converted on our servers, and then you download the JSON. The download link expires after 30 minutes.

The message names the problem and the line, like mismatched tag at line 3. Causes include a closing tag that doesn't match its opening tag, an & that should be &amp;, or a second root element after the first one closes. Fix that line and upload the file again.

Convert XML to JSON online free

Turn an XML feed or export into JSON you can work with

A lot of data still arrives as XML: product feeds, order exports from older shop systems, SOAP responses, RSS and settings files. The code you want to read it with, in JavaScript, Python or an API call, is far happier with JSON. Converting XML to JSON gets you there without writing a parser, and this page's converter is built for exactly that: upload the XML file, download a JSON file with the same data in it.

The two formats don't line up one to one. XML has attributes and JSON doesn't, XML can repeat a tag and JSON uses a list for that, and XML treats every value as text. Each of those needs a rule, and the rules decide what your JSON looks like. They're all below, with a real file we converted ourselves.

How to convert XML to JSON

  1. Upload your XML file - Drop it into the box at the top of this page, or browse for it. You can add up to 5 files at once.
  2. Check the output - JSON is already picked on this page, so there's nothing to set.
  3. Download your file - Save the .json file when it's ready. Any text editor or browser opens it.

The conversion runs on our servers, not in your browser, so there's nothing to install and no account to make. Our security page explains what happens to your file afterwards.

How XML tags become JSON keys

The converter reads the whole document and writes one JSON key for each tag name. The output is indented with two spaces and saved as UTF-8 without a byte order mark, so Hindi, Japanese or Arabic text is written as the characters themselves, not as \u codes. The shape follows these rules:

  • The root element becomes the top key. An XML file has one outer tag that holds everything, and the JSON is one object with that tag's name as its only key: <library>...</library> gives {"library": {...}}.
  • Attributes become keys with an @_ prefix. <book id="b1"> gives "@_id": "b1". The prefix is there so an attribute called name can't collide with a child tag called name. Attributes come first in the object, in the order they appear in the tag, and their values are trimmed.
  • Text becomes a string, or #text. A tag with nothing but text inside, like <title>Dune</title>, becomes "title": "Dune". If the tag also has attributes or child tags, the text has to share the object with them, so it goes under the key #text, always last.
  • Repeated tags become a list. Two or more <book> tags inside the same parent give "book": [...], with one object per tag, in document order.
  • Empty tags become an empty string. <note/> and <note></note> both give "note": "". A tag with attributes and nothing else gives an object holding only those attributes, such as {"@_code": "7"}.
  • Namespace prefixes stay in the key. <x:item> gives the key "x:item", an attribute x:attr gives "@_x:attr", and the xmlns:x declaration itself is kept as "@_xmlns:x", so nothing about the namespace is lost.

Here's a short file that uses the first three rules, and the JSON our live converter gave for it:

<product sku="A1">
  <name>Lamp</name>
  <price currency="EUR">19.99</price>
  <note/>
</product>
{
  "product": {
    "@_sku": "A1",
    "name": "Lamp",
    "price": {
      "@_currency": "EUR",
      "#text": "19.99"
    },
    "note": ""
  }
}

These are the same conventions the fast-xml-parser library uses, and our converter was written to match its output. If you already have code that reads fast-xml-parser's JSON, it reads ours too.

A real example from our tests

On 13 November 2026 we uploaded this file to our live converter. It's a small book catalog: two <book> tags, each with an id and a lang attribute, a price that carries a currency attribute, and an ISBN that starts with a zero.

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
  <book id="bk101" lang="en">
    <title>The Hobbit</title>
    <author>J.R.R. Tolkien</author>
    <price currency="USD">8.99</price>
    <isbn>0261102214</isbn>
  </book>
  <book id="bk102" lang="fr">
    <title>Le Petit Prince</title>
    <author>Antoine de Saint-Exupéry</author>
    <price currency="EUR">7.50</price>
    <isbn>0156012197</isbn>
  </book>
</catalog>

About 17 seconds after we started the upload, it came back as this JSON:

{
  "catalog": {
    "book": [
      {
        "@_id": "bk101",
        "@_lang": "en",
        "title": "The Hobbit",
        "author": "J.R.R. Tolkien",
        "price": {
          "@_currency": "USD",
          "#text": "8.99"
        },
        "isbn": "0261102214"
      },
      {
        "@_id": "bk102",
        "@_lang": "fr",
        "title": "Le Petit Prince",
        "author": "Antoine de Saint-Exupéry",
        "price": {
          "@_currency": "EUR",
          "#text": "7.50"
        },
        "isbn": "0156012197"
      }
    ]
  }
}

Everything landed where the rules say. catalog is the top key. The two attributes on each book became @_id and @_lang, ahead of the child tags. The price had both an attribute and text, so it became an object with @_currency and #text. The two <book> tags became a list of two objects. Every value is a string, which is why 8.99 is in quotes and the ISBN 0261102214 still has its leading zero; a JSON number would have dropped it. The accent in Saint-Exupéry came through as written. You can download the XML we uploaded and the JSON it produced and compare them yourself.

The backend team ran their own production test the same day. A file that declared encoding="ISO-8859-1" instead of UTF-8 converted with São Paulo intact, an orders export with namespaces and CDATA came out as described above, and a file with Japanese, Arabic, Hindi and emoji text came through unchanged.

Things that catch people out

  • A tag that appears once is not a list. The converter can't know that <item> is meant to repeat if a record only has one. So in the file below, the first order has "item": ["Lamp", "Desk"] and the second has "item": "Chair", a plain string. The backend team tested this case, and we ran the file below through our live converter on 13 November 2026 to check it. Code that reads the JSON has to handle both shapes, for example by wrapping a non-list value in a list before looping over it.
<orders>
  <order id="1">
    <item>Lamp</item>
    <item>Desk</item>
  </order>
  <order id="2">
    <item>Chair</item>
  </order>
</orders>
{
  "orders": {
    "order": [
      {
        "@_id": "1",
        "item": [
          "Lamp",
          "Desk"
        ]
      },
      {
        "@_id": "2",
        "item": "Chair"
      }
    ]
  }
}
  • Every value is a string. XML has no number type, so the converter doesn't guess. <qty>2003</qty> gives "2003", not 2003, and true stays the word "true". Convert the fields you know are numbers in your own code, where you also know which ones must keep their zeros.
  • Text around an inline tag is joined with nothing between. In document-style XML, like <p>before<b>bold</b>after</p>, the result is "b": "bold" and "#text": "beforeafter". The order of words around the inline tag is lost, so this converter suits data files better than marked-up prose.
  • Whitespace is trimmed at both ends, and kept inside. The indentation around a value goes away, but line breaks in the middle of a multi-line value stay as written.
  • CDATA is kept as plain text. A <![CDATA[ ... ]]> block comes through exactly as written, spaces included, and any & or < inside it is a normal character in the JSON string.
  • Comments, processing instructions and the declaration are dropped. <!-- --> comments, a line like <?xml-stylesheet ?>, and the <?xml version="1.0"?> header have no place in JSON, so they aren't in the output. Built-in and numeric entities are decoded first: &amp;, &#233; and &#x263A; give &, é and ☺.
  • A DOCTYPE that declares entities is refused. Entity declarations are how the XXE and "billion laughs" attacks work, so the converter stops with This XML declares entities, which we don't accept for security reasons. A plain <!DOCTYPE root> line is fine. We fed it both attack files on our test server before launch, and it refused both. An entity the file uses but never declares, such as &nbsp;, gives Invalid XML: undefined entity at line N., which we checked on the test server too.
  • Other encodings work when the file says which one it uses. UTF-8 is the default, with or without a byte order mark. A file whose header declares another encoding is read in that encoding; ISO-8859-1 was tested on production, Shift_JIS and UTF-16 on our test server before launch. A file that isn't UTF-8 and doesn't declare anything is refused with Invalid XML: the file isn't UTF-8 text and doesn't say which encoding it uses.

Limits, and files that won't convert

  • Each XML file can be up to 20 MB. The upload box takes bigger files, but the converter stops anything over 20 MB with This file is larger than 20 MB. Try splitting it into smaller files.
  • A file with more than 2,000,000 elements, or tags nested more than 64 levels deep, is refused. The converted file can't be larger than 200 MB, and a conversion that runs past 960 seconds is stopped. Each of these has its own message asking you to split the file.
  • An empty file gives This file is empty.
  • Broken XML is refused with the reason and the line, for example Invalid XML: mismatched tag at line 3. Plain text saved with an .xml name gets Invalid XML: syntax error at line 1. Fix that line in a text editor and upload the file again.

If you want rows and columns rather than nested objects, our XML to CSV converter picks the repeating tag as the rows and flattens each record into dotted column names. And to go the other way, the JSON to XML converter reads the same @_ and #text keys back into attributes and text.

Last updated: 13 November 2026 · ChangeMyFile

What changed: New page, added when JSON support launched.

Tested: 13 November 2026, on our live converter: our sample XML book catalog became the JSON shown above in about 17 seconds. The root <catalog> tag became the top key, the id and lang attributes became @_id and @_lang, the price with a currency attribute became an object with @_currency and #text, the two <book> tags became a list, and every value came out as a string, so the ISBN 0261102214 kept its zero.

RelatedAlso convert to JSON from