XML to JSON Converter

Convert XML to JSON and JSON back to XML in your browser, with attributes kept as "@" keys and repeated tags turned into arrays.

XML Input
JSON Output

How to Convert XML to JSON

Paste XML into the left panel and the JSON appears as you type. Parsing uses your browser's own XML parser — the same one that handles XHTML and SVG — so it validates well-formedness properly and reports the line where a document breaks, rather than accepting something malformed and producing quiet nonsense.

Everything runs locally. Your document is never uploaded, logged, or sent to a server, which matters because the XML developers most often need to convert is a SOAP response or a partner feed containing credentials, order records, or personal data.

The Mapping Convention

XML and JSON do not describe the same shape of data, so every converter has to invent a convention. This one is stated up front and applied without exception:

XMLJSON
Attribute id="bk101""@id": "bk101"
Element with only textThe string itself
Element with text and attributes or children"#text" alongside the other keys
A tag appearing more than onceAn array
Empty element <a/>null
<![CDATA[...]]>Merged into the element's text
XML in
<book id="bk101">
  <title>XML Guide</title>
  <price>44.95</price>
</book>
JSON out
{
  "book": {
    "@id": "bk101",
    "title": "XML Guide",
    "price": "44.95"
  }
}

The @ prefix exists because XML has two ways to attach a value to an element and JSON has one. Without it, an attribute named title and a child element named title would collide. The prefix keeps them distinct and makes the conversion reversible — switch to JSON → XML and any key beginning with @ becomes an attribute again.

Three Things That Make XML to JSON Genuinely Hard

1. Everything is a string, and no converter can know otherwise. <price>44.95</price> becomes "44.95", not 44.95. This is not a limitation of the tool — XML has no type system at all. Types live in an external XSD schema that is not part of the document, so a converter reading the document alone has nothing to work from. Converters that guess will eventually turn a product code like 0012 into 12. Cast on your side, where you know the schema.
2. One item and many items produce different shapes. A tag repeated twice becomes an array; the same tag appearing once stays a plain object. So an order with two line items gives you items.item[0], and an order with one gives you items.item — and code written against the first crashes on the second. This is the single most common bug in XML-to-JSON pipelines. Defend against it by normalising on read: const list = [].concat(parsed.items.item ?? []).
3. Comments, processing instructions, and the DTD are dropped. JSON has nowhere to put them. The XML declaration is regenerated on the way back rather than preserved, and a DOCTYPE is not carried across. If the document is a contract with an external system, keep the XML as the source of truth and treat the JSON as a view of it.

XML to JSON Examples

Three documents that cover most of what an XML to JSON converter meets in practice. Each output below was produced by pasting the XML into the tool above.

An RSS item. The guid element has both an attribute and text, so its text moves to #text. The CDATA section arrives as a plain string with its contents untouched: the &amp; inside it stays &amp;, because CDATA is not entity-decoded.

<rss version="2.0">
  <channel>
    <title>Stackiox Blog</title>
    <item>
      <title>XML to JSON in practice</title>
      <link>https://example.com/xml-json</link>
      <description><![CDATA[<p>Attributes, arrays &amp; CDATA.</p>]]></description>
      <guid isPermaLink="false">post-42</guid>
    </item>
  </channel>
</rss>
{
  "rss": {
    "@version": "2.0",
    "channel": {
      "title": "Stackiox Blog",
      "item": {
        "title": "XML to JSON in practice",
        "link": "https://example.com/xml-json",
        "description": "<p>Attributes, arrays &amp; CDATA.</p>",
        "guid": {
          "@isPermaLink": "false",
          "#text": "post-42"
        }
      }
    }
  }
}

A SOAP response. The namespace prefix stays on the key, the namespace declarations become @xmlns attributes, and every value, including the total and the quantity, is a string. Switch to JSON → XML and paste the output back: this document comes out identical to the input, plus the XML declaration.

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetOrderResponse xmlns="urn:shop">
      <order id="1001" status="shipped">
        <total currency="EUR">89.90</total>
        <item sku="A-17">2</item>
      </order>
    </GetOrderResponse>
  </soap:Body>
</soap:Envelope>
{
  "soap:Envelope": {
    "@xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/",
    "soap:Body": {
      "GetOrderResponse": {
        "@xmlns": "urn:shop",
        "order": {
          "@id": "1001",
          "@status": "shipped",
          "total": {
            "@currency": "EUR",
            "#text": "89.90"
          },
          "item": {
            "@sku": "A-17",
            "#text": "2"
          }
        }
      }
    }
  }
}

Mixed content. Text interleaved with child elements is the one shape JSON cannot hold faithfully. <p>Hello <b>world</b>, again<!-- note --><br/></p> converts to:

{
  "p": {
    "b": "world",
    "br": null,
    "#text": "Hello , again"
  }
}

The text fragments are joined into one #text value, their position relative to <b> is lost, and the comment is dropped. That is fine for data documents and wrong for prose. If the XML is really XHTML or a document format, convert it with the HTML to Markdown converter instead.

Namespaces

Namespace-qualified tags keep their prefix exactly as written: <soap:Envelope> becomes the key "soap:Envelope", and the declaration itself survives as the attribute key "@xmlns:soap". Nothing is resolved or rewritten, so a round trip through JSON and back produces a document with the same prefixes it started with.

This is the pragmatic choice rather than the theoretically correct one. Namespace prefixes are arbitrary labels — two documents using soap: and s: for the same namespace URI are equivalent to a validating parser but produce different JSON keys here. If you are consuming feeds from multiple vendors, match on the local name rather than the full prefixed key.

Converting JSON to XML

Switch direction and the convention runs backwards: @ keys become attributes, #text becomes the element's text content, arrays become repeated tags, and null becomes a self-closing element. Output is indented two spaces and carries an XML declaration.

A JSON object with exactly one top-level key uses that key as the root element, since XML permits exactly one. Several top-level keys are wrapped in a <root> element instead, because there is no other legal option.

Element names are validated rather than silently repaired. XML names cannot contain spaces and must begin with a letter or underscore, so a key like "first name" is reported as an error instead of being quietly rewritten to first_name — a rename would produce a document that no longer matches the schema it was headed for, and you would find out much later.

Escaping is handled for you, and only where the specification requires it: &, <, and > are escaped in element text, and attribute values additionally escape the double quote that would otherwise close them. A quote or apostrophe sitting in ordinary text is left alone, because escaping it would be noise rather than correctness. A document converted to JSON and straight back comes out byte-for-byte identical, entities included.

Convert XML to JSON in Code

To convert XML to JSON inside an application rather than in the browser, use a library that follows the same convention, so the JSON you prototype here matches what your code produces. All three examples below convert this file:

<order id="1001" status="shipped">
  <total currency="EUR">89.90</total>
  <item sku="A-17">2</item>
</order>

Python — xmltodict uses the same @ and #text convention as this tool, so its output is identical to the converter's:

import json, xmltodict

with open("order.xml") as f:
    data = xmltodict.parse(f.read())
print(json.dumps(data, indent=2))
{
  "order": {
    "@id": "1001",
    "@status": "shipped",
    "total": {
      "@currency": "EUR",
      "#text": "89.90"
    },
    "item": {
      "@sku": "A-17",
      "#text": "2"
    }
  }
}

Pass force_list=("item",) to xmltodict.parse to make item an array even when there is only one, which removes the one-versus-many bug described above.

Node.js — fast-xml-parser needs three options to match. With its defaults, the file above becomes {"order":{"total":89.9,"item":2}}: the attributes are gone and 89.90 has been turned into the number 89.9.

import { readFileSync } from 'node:fs';
import { XMLParser } from 'fast-xml-parser';

const parser = new XMLParser({
  ignoreAttributes: false,     // the default drops every attribute
  attributeNamePrefix: '@',
  parseTagValue: false,        // the default turns "0012" into 12
  isArray: (name) => name === 'item',
});
const data = parser.parse(readFileSync('order.xml', 'utf8'));

With those options the output matches the tool's, except that item is always an array and attribute keys come after the other keys, which JSON consumers do not care about.

JavaScript in the browser — no library needed. DOMParser is the parser this tool uses, and this short function applies the same mapping. On 1,000 randomly generated documents without mixed content it produced exactly the same JSON as the converter:

function toJson(el) {
  const obj = {};
  for (const a of el.attributes) obj['@' + a.name] = a.value;
  for (const child of el.children) {
    const v = toJson(child);
    const k = child.nodeName;
    obj[k] = Object.hasOwn(obj, k) ? [].concat(obj[k], v) : v;
  }
  const text = el.children.length ? '' : el.textContent.trim();
  if (!Object.keys(obj).length) return text || null;
  if (text) obj['#text'] = text;
  return obj;
}

const doc = new DOMParser().parseFromString(xmlString, 'application/xml');
if (doc.querySelector('parsererror')) throw new Error('Invalid XML');
const json = { [doc.documentElement.nodeName]: toJson(doc.documentElement) };

Object.hasOwn matters here: a plain in check would treat an element named <constructor> or <toString> as already present, because those names exist on every object's prototype.

Why This Conversion Still Comes Up

XML was declared legacy years ago and remains everywhere it was already installed. Three cases account for most conversions.

SOAP and enterprise APIs. Banking, insurance, logistics, government, and healthcare integrations are overwhelmingly SOAP, and they are not being rewritten. Converting the envelope to JSON at the edge lets the rest of your codebase work in one format. The credentials in those envelopes are exactly why a converter that uploads your payload is the wrong tool.

RSS, Atom, and sitemaps. Feed formats are XML by specification, and the body of each item is usually HTML wrapped in CDATA; to republish an item in a Markdown-based blog or docs site, paste that HTML into our HTML to Markdown converter. Anything that ingests feeds — readers, aggregators, monitoring — converts them on the way in. A sitemap is only useful once crawlers know where it is, which is one Sitemap: line in robots.txt; our robots.txt generator writes that line and lets you test which paths each crawler may fetch. Feed polling is scheduled work by nature, and polling too aggressively is the fastest way to get rate-limited by a publisher; our cron job generator turns an interval into a valid expression and shows you when it would actually fire.

Configuration in older toolchains. Maven pom.xml, Android manifests, .NET web.config, and Ant build files are XML. Converting to JSON makes them scriptable with ordinary tooling; if you are migrating that config to a modern format, our YAML ↔ JSON converter is the second half of the trip, since most current toolchains expect YAML.

Checking the Result

XML documents nest far more deeply than most JSON, and a converted SOAP response frequently arrives six or seven levels down inside envelope and body wrappers. Paste the output into our JSON formatter and validator — its depth and key counts tell you immediately whether you are looking at the payload or at the envelope wrapped around it. If the result is a list of records headed for a spreadsheet, JSON ↔ CSV will flatten it into columns.

Integrating with a legacy XML API? Uptime and response monitoring from Better Stack can assert on response content, so a partner feed that starts returning an error envelope with a 200 status pages you instead of silently corrupting a nightly import. These are affiliate links — they cost you nothing and help keep these tools free.

Frequently Asked Questions

Is my XML uploaded anywhere?
No. Parsing and conversion run entirely in your browser using the built-in DOMParser. Nothing is transmitted, stored, or logged, so it is safe to paste SOAP envelopes containing credentials.
Why are my numbers quoted in the JSON?
Because XML has no types — every value in the document is text, and type information lives in a separate XSD schema the converter never sees. Guessing would corrupt identifiers with leading zeros, so nothing is coerced.
What does the @ prefix on some keys mean?
That key was an XML attribute rather than a child element. The prefix prevents an attribute and an element with the same name from colliding, and it lets the JSON convert back to equivalent XML.
Why did a repeated element become an array in one document but not another?
Because arrays are inferred from the document, and a single occurrence is indistinguishable from a non-repeating element without a schema. Normalise with [].concat(value) on read — this is the classic XML-to-JSON pitfall.
Does it handle CDATA sections?
Yes. CDATA content is merged into the element's text exactly as written, so escaped HTML or embedded markup inside a feed item comes through intact.
Can it convert a document with no single root?
No — an XML document must have exactly one root element, and a fragment with several siblings at the top is not well-formed. Wrap it in a container element first.
How do I convert XML to JSON online?
Paste the XML into the left panel of the converter above and the JSON appears on the right as you type. Attributes become @ keys, repeated tags become arrays, and CDATA becomes a string. Copy the result, or press Use as input to convert it back to XML. The conversion runs in your browser, so nothing is uploaded.
Is XML to JSON conversion lossless?
For data documents it is close: attributes, text, CDATA, namespaces and repeated elements all survive, and a document without comments or mixed content converts back to the same XML, plus an XML declaration. Comments, processing instructions and the DOCTYPE are dropped, all values become strings, and mixed content such as Hello <b>world</b>, again loses the order of its text fragments.
Can I convert an XSD to JSON?
You can paste an XSD file here, because a schema is itself an XML document, and you will get its structure as JSON: xs:element, xs:complexType and their attributes. That is not the same as a JSON Schema. Translating XSD types and constraints into JSON Schema needs a dedicated schema converter, and some XSD features have no JSON Schema equivalent.