XML to JSON Converter

Output
Examples:

Last updated: 2026-09-17

Paste XML and get formatted JSON output. XML attributes are prefixed with @_ by default. You can control attribute prefixing, array detection, namespace handling, and whether tag values are parsed as numbers/booleans. All processing happens in your browser.

What this tool does

Converts an XML document into formatted JSON. XML attributes are prefixed with @_ by default. Repeated child elements become JSON arrays. You can toggle namespace stripping and number/boolean parsing. The conversion uses fast-xml-parser and runs entirely in your browser.

Example

Input:

<user id="123" role="admin">
  <name>Alice</name>
  <email>alice@example.com</email>
</user>

Output:

{
  "user": {
    "@_id": "123",
    "@_role": "admin",
    "name": "Alice",
    "email": "alice@example.com"
  }
}

How XML maps to JSON

XML constructJSON representationExample
Element with textString value<name>Alice</name> → "name": "Alice"
AttributeKey with @_ prefix<user id="123"> → "@_id": "123"
Repeated elementsJSON array<item>A</item><item>B</item> → "item": ["A", "B"]
Text between elements#text key<a>Hello<b/></a> → "#text": "Hello"
Self-closing elementEmpty string<tag/> → "tag": ""
Namespace prefixPreserved or stripped<ns:user> → "ns:user" or "user"

All values are strings by default. Enable Parse numbers and booleans to convert numeric and boolean-looking values to their JSON equivalents. Whitespace is handled for you: leading and trailing spaces inside every tag value are trimmed automatically, so <name> Alice </name> becomes "Alice". Whitespace between words inside a value is preserved.

Namespaces

XML namespaces are preserved in the output by default. An element like <ns:user xmlns:ns="http://example.com"> produces a key ns:user in the JSON. Enable Strip namespaces to remove the ns: prefix and the xmlns declarations, producing cleaner JSON when you do not need namespace information.

Mixed content

XML with mixed content (text and child elements interleaved) is handled, but the output structure may not perfectly represent the ordering. Text between elements is accessible via the #text key. For documents with complex mixed content (like XHTML), consider whether JSON is the right target format.

CDATA sections

CDATA sections are preserved as text content in the JSON output. The parser treats CDATA and regular text identically, so you do not need to handle them separately.

XML entity references

XML lets you escape characters that would otherwise break the markup. The converter decodes the standard entity set automatically because fast-xml-parser runs with entity processing enabled. You do not need to toggle anything.

Input:

<message>Tom &amp; Jerry &lt;3</message>

Output:

{
  "message": "Tom & Jerry <3"
}

The decoded text is an ordinary string in the JSON, so downstream code reads it directly with no extra unescaping. One caveat: custom entities declared in a DTD (for example <!ENTITY company "Acme">) are not resolved. The parser does not load external DTDs, and this tool does not pass a custom entity map, so an unresolved entity triggers a parse error. Stick to the five built-in entities and numeric references, or expand your custom entities before pasting the XML.

Converting SVG to JSON

SVG is XML, so it converts like any other document. Attributes become @_-prefixed keys, nested elements become nested objects, and text inside an element lands under #text.

Input:

<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <rect x="10" y="10" width="80" height="80" fill="blue"/>
  <text x="50" y="55" font-size="12">Hi</text>
</svg>

Output (defaults, namespaces preserved):

{
  "svg": {
    "@_xmlns": "http://www.w3.org/2000/svg",
    "@_width": "100",
    "@_height": "100",
    "rect": {
      "@_x": "10",
      "@_y": "10",
      "@_width": "80",
      "@_height": "80",
      "@_fill": "blue"
    },
    "text": {
      "@_x": "50",
      "@_y": "55",
      "@_font-size": "12",
      "#text": "Hi"
    }
  }
}

A few notes specific to SVG. The default xmlns declaration becomes @_xmlns because it is an attribute like any other. Attribute names keep their original characters, so font-size becomes @_font-size with the hyphen intact. Coordinate and size values stay strings unless you enable Parse numbers and booleans, in which case "100" becomes the number 100. For most drawing code you want coordinates as strings, so leave parsing off. SVG with many nested <g> groups produces deeply nested JSON; if you only need a flat list of shapes, strip namespaces first and post-process the result to walk the tree.

Converting SOAP responses

SOAP envelopes wrap the actual payload in Body and Header elements and almost always carry namespace prefixes. The converter handles the nesting directly; the only decision is whether to keep the prefixes.

Input:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <m:GetPriceResponse xmlns:m="http://example.com/pricing">
      <m:Price>42.50</m:Price>
      <m:Currency>USD</m:Currency>
    </m:GetPriceResponse>
  </soap:Body>
</soap:Envelope>

Output with namespaces preserved:

{
  "soap:Envelope": {
    "@_xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/",
    "soap:Body": {
      "m:GetPriceResponse": {
        "@_xmlns:m": "http://example.com/pricing",
        "m:Price": "42.50",
        "m:Currency": "USD"
      }
    }
  }
}

Output with Strip namespaces enabled:

{
  "Envelope": {
    "Body": {
      "GetPriceResponse": {
        "Price": "42.50",
        "Currency": "USD"
      }
    }
  }
}

For SOAP, stripping namespaces is usually what you want. The prefixes carry no data, and removing them lets you write result.Envelope.Body.GetPriceResponse.Price instead of bracket notation with soap: and m:. If two namespaces define the same local name and stripping would collide them, keep namespaces on and disambiguate by prefix. Note that 42.50 stays a string unless you enable parsing; if your consumer does math on the price, turn Parse numbers and booleans on or cast it yourself.

Parsing numbers and booleans

By default every tag value and attribute value is a string. Enable Parse numbers and booleans and the converter inspects each value: if it matches a number it becomes a JSON number, and if it equals true or false it becomes a JSON boolean. Internally this sets parseTagValue and parseAttributeValue together, so the same toggle governs both tag text and attribute text.

Input:

<sensor id="3" active="true">
  <temperature>21.5</temperature>
  <humidity>64</humidity>
  <label>sensor-3</label>
</sensor>

Output with parsing enabled:

{
  "sensor": {
    "@_id": 3,
    "@_active": true,
    "temperature": 21.5,
    "humidity": 64,
    "label": "sensor-3"
  }
}

label stays a string because sensor-3 is not a valid number. This convenience has a cost. Identifiers that look like numbers lose their original representation: an id of 007 becomes the number 7, dropping the leading zeros. A boolean attribute written as true or false becomes a real boolean, but attributes written as 1 and 0 stay strings because the parser does not coerce those to booleans. Values written in scientific notation like 1e3 become numbers, which is usually welcome. When in doubt, leave parsing off and cast values explicitly in your consuming code where the intent is unambiguous.

What the converter drops automatically

Some XML constructs carry no useful information for JSON and are removed before the object tree is built. You do not toggle these.

The practical effect is that you can paste a full document, including its prolog and stylesheet instructions, without first trimming them by hand. The output starts at your root element. If you rely on the encoding declared in the prolog, note that the converter assumes the input is already a Unicode string; it does not transcode bytes.

Force array option

XML has no way to mark an element as a list of one. <items><item>A</item></items> and <items><item>A</item><item>B</item></items> look structurally different: the first yields a single object, the second yields an array. Code that always expects an array breaks on the single-item case.

The converter auto-detects repeated siblings and turns them into arrays. For tags that appear once but must still be arrays in your consumer, use the Force array option. It takes a list of tag names; every occurrence of those tags is wrapped in an array, even when there is only one.

Input:

<orders>
  <order id="1">A</order>
</orders>

Without force array:

{
  "orders": {
    "order": {
      "@_id": "1",
      "#text": "A"
    }
  }
}

With order added to Force array:

{
  "orders": {
    "order": [
      {
        "@_id": "1",
        "#text": "A"
      }
    ]
  }
}

List only leaf or item-level tag names you know are logically arrays: item, order, line, phone, email, attachment. Forcing array on a container like orders wraps the whole container, which is rarely what you want. The match runs on the exact tag name after namespace stripping, so if you enable Strip namespaces, list the un-prefixed names. Repeated elements are already arrays automatically, so you only need force array for tags that might appear once.

Do it in code

Node.js (fast-xml-parser):

const { XMLParser } = require('fast-xml-parser');
const parser = new XMLParser({ attributeNamePrefix: '@_', textNodeName: '#text' });
const result = parser.parse(xmlString);
console.log(JSON.stringify(result, null, 2));

Python (xmltodict):

import xmltodict, json
doc = xmltodict.parse(xml_string)
print(json.dumps(doc, indent=2))

Note: xmltodict uses @ as the default attribute prefix (not @_).

CLI (xml2json):

cat data.xml | xml2json | jq '.'

Install with npm install -g xml2json-command-line, or use yq -p xml -o json '.' data.xml with the Go-based yq.

Choosing options for large feeds

Big feeds such as RSS, Atom, and product catalogs share a shape: one root, a repeating item element, and attributes carrying metadata. A sensible default configuration for these is to strip namespaces, keep attributes with the @_ prefix, leave parsing off, and force array on the repeating item tag. With namespaces gone you avoid long dotted keys; with parsing off you avoid corrupting identifiers and publication dates that happen to be numeric; with force array on the item tag your loop always receives a list even when a feed contains a single entry.

Attributes are usually few and meaningful in feeds (GUIDs, URLs, timestamps), so keeping them is worth the prefix. If a feed uses attributes only for namespace declarations and you have already stripped namespaces, enabling Ignore attributes produces a smaller object with no @_ keys at all. Measure both shapes on your actual data before committing; the difference in object size and traversal cost can be significant when you process thousands of entries.

Edge cases handled

Validation and errors

The converter validates before and during parsing. If the input does not begin with a < character it rejects the document early with a message that the input does not appear to be valid XML. During the parse, fast-xml-parser reports structural problems: unclosed tags, mismatched closing tags, malformed attributes, and unresolvable entities. Each error surfaces as a message in the output panel instead of JSON.

The validator does not enforce a schema. There is no XSD or DTD checking, so a structurally valid document whose content violates your business rules parses without complaint. Length limits, required fields, allowed enumerations, and cross-field constraints are your responsibility downstream. The converter also does not fetch external resources, so external entity references and external DTD subsets fail rather than silently expanding, which keeps the operation safe and offline.

Round-tripping back to XML

If you convert XML to JSON and then feed that JSON into the JSON-to-XML tool, you get a document back, but it is not byte-identical to the original. Information is lost or reshaped in one direction. The XML declaration, comments, processing instructions, and DOCTYPE were dropped on the way in, so they cannot reappear. Attribute order within a tag is not preserved by JSON objects, so the regenerated tag lists attributes in an arbitrary order. Mixed content collapses to a single #text string, so the relative positions of text and child elements are gone. Whitespace between attributes and indentation are not retained.

For data-shaped XML such as configuration files, API payloads, and feeds where every element contains either text or child elements but not both, the round trip is close enough for diffing and editing. For document-shaped XML such as XHTML, DocBook, or hand-authored prose with inline markup, treat the conversion as one-way.

Common mistakes when converting XML to JSON

Frequently asked questions

How are XML attributes represented in JSON?
By default, attributes are prefixed with @_ so they are distinguishable from child elements. For example, <user id="123"> becomes {"user": {"@_id": "123"}}. You can customize the prefix or ignore attributes entirely.
How does array detection work?
XML does not distinguish between a single child element and an array of one. The converter treats repeated child elements as arrays automatically. A single <item> becomes a string, while multiple <item> elements become a JSON array.
What does "Strip namespaces" do?
It removes namespace prefixes from element names. For example, <ns:user> becomes <user>, and xmlns declarations are removed. This produces cleaner JSON when namespaces are not needed.
Should I enable "Parse numbers & booleans"?
Enable this to automatically convert tag values that look like numbers or booleans. For example, <count>42</count> becomes 42 (number) instead of "42" (string). Disable it if you want all values as strings, which is safer for data like zip codes or IDs with leading zeros.
How are empty elements handled?
An empty XML element like <tag/> or <tag></tag> becomes an empty string in the JSON output. This is consistent with how most XML-to-JSON converters handle self-closing tags.

Related converters