Configurable attribute namingArray forcingPositioned XML errors

XML to JSON Converter

Convert XML into JSON with your choice of attribute prefix, text node key and array handling — and a clear account of what the mapping had to decide. All processing happens locally in your browser.

Structural conversion

Convert XML into JSON

Attributes, text nodes and repeated elements have no direct JSON equivalent, so the naming convention for each is yours to choose. Malformed XML is reported with a line and column.

XML elements3
Attributes5
Max depth2
Size change+84%
InputXML
OutputJSON
{
  "catalogue": {
    "book": [
      {
        "#text": "The Pragmatic Programmer",
        "@id": "bk101",
        "@lang": "en"
      },
      {
        "#text": "Refactoring",
        "@id": "bk102",
        "@lang": "en"
      }
    ],
    "@version": "2"
  }
}
Mapping options
Widely used; matches many XML-to-JSON conventions.
Used when an element has both text and attributes.

A single occurrence of an element becomes a plain value; two or more become an array. That means the JSON shape can change between documents from the same schema.

The one-element problemThis is the biggest hazard in XML to JSON conversion. Two documents valid against the same schema can produce different JSON shapes: one <server> gives you an object, two give you an array. Code written against the two-element case then crashes on the one-element case. Turn on array forcing, or normalise the shape after parsing.
Mapping decisionsA single occurrence of an element becomes a plain value, not a one-item array. If the schema allows repetition, enable array forcing so the shape stays consistent.
Load sampleLoad a sample

What is the hard part of converting XML to JSON?

XML can express three things JSON has no syntax for, and each needs a naming convention invented for it. There is no single correct answer, which is why this page makes each one a choice rather than a fixed behaviour.

XML feature Problem Convention used
Attributes JSON objects have keys, not attributes A prefix such as @id
Text alongside children An object cannot also be a string A reserved key such as #text
Repeated elements One occurrence looks like a value, two look like a list Array when repeated, or always
Namespaces A colon in a key is awkward Keep the prefix, or strip it
Comments JSON has none Discarded
Everything is text JSON has real types Left as strings unless you opt in

The one-element problem

This is the single biggest hazard in XML to JSON conversion, and it causes production bugs rather than cosmetic ones.

Consider a schema where <server> may repeat. Two documents, both valid:

<servers>                    <servers>
  <server>                     <server>
    <host>alpha</host>           <host>only</host>
  </server>                    </server>
  <server>                   </servers>
    <host>beta</host>
  </server>
</servers>

They produce structurally different JSON:

{ "servers": { "server": [ {"host":"alpha"}, {"host":"beta"} ] } }

{ "servers": { "server": {"host":"only"} } }

In the first, server is an array. In the second it is an object. Code written against the first — data.servers.server.map(...) — throws on the second. The failure appears only when a customer happens to have exactly one server, which is why it tends to reach production.

Array forcing solves it by wrapping every element in an array regardless of count. The output is more verbose, but the shape is stable, and consuming code can be written once:

{ "servers": [ { "server": [ {"host":["only"]} ] } ] }

The alternative, if you control the consumer, is to normalise after parsing: const list = [].concat(data.servers.server ?? []). Either approach works; silently hoping every document has two or more children does not.

How to convert

  1. Paste your XML The document is validated first, so a malformed one is reported with a line and column rather than producing partial output.
  2. Choose the attribute prefix @ is the most widely used. Removing the prefix entirely gives cleaner keys but lets an attribute collide with a child element of the same name.
  3. Decide on array forcing Off matches the shape of this one document. On gives a shape that is stable across documents.
  4. Review the mapping notes Comments, CDATA, namespaces, DOCTYPE and mixed content are each reported when present.

Examples: attributes and mixed content

An element with both attributes and text needs both to land somewhere:

<catalogue version="2">
  <book id="bk101" lang="en">The Pragmatic Programmer</book>
</catalogue>

becomes

{
  "catalogue": {
    "book": {
      "#text": "The Pragmatic Programmer",
      "@id": "bk101",
      "@lang": "en"
    },
    "@version": "2"
  }
}

Note that the element's own text is now under a key rather than being the value. That is unavoidable: book has to be an object to hold the attributes, and an object cannot simultaneously be a string.

The collision case for unprefixed attributes

With the prefix removed, this XML becomes ambiguous:

<item type="a">
  <type>b</type>
</item>

Both the attribute and the child element want the key type, and only one can win. Keeping a prefix is the reason the convention exists.

Empty elements

<empty/>              →  "empty": ""
<flag enabled="true"/>  →  "flag": { "@enabled": "true" }

An empty element becomes an empty string rather than null, because XML draws no distinction between "empty" and "absent" in the way JSON does.

Types: why values stay strings by default

XML has no types. <port>8080</port> contains the three characters 8080, and only a schema — an XSD, if one exists — says it means an integer. Without the schema, converting is guesswork.

Type conversion is therefore off by default, because it loses data in predictable ways:

XML value As a string With conversion on
8080 "8080" 8080
0123 "0123" 123 — leading zero lost
1.0 "1.0" 1 — trailing zero lost
+44 20 1234 "+44 20 1234" Unchanged, not numeric
true "true" true

The leading-zero rows are the ones that cause real damage: postcodes, account numbers, product codes and zero-padded IDs all lose information. If you enable conversion, check any zero-padded field in the output.

Namespaces, CDATA and things that are dropped

  • Namespace prefixes are kept by default, so <cfg:name> becomes the key cfg:name. Stripping them gives cleaner keys but merges two namespaces that share a local name — a real risk in SOAP documents.
  • Namespace declarations appear as ordinary attributes, so xmlns:cfg shows up as @xmlns:cfg. Ignoring attributes removes them.
  • CDATA sections are unwrapped to their text. <![CDATA[5 < 10]]> becomes the string 5 < 10. The marker itself has no JSON representation, so re-serialising to XML will escape the characters as entities instead.
  • Comments are discarded entirely.
  • Processing instructions and the XML declaration are dropped, since they describe the document rather than its data.
  • DOCTYPE is ignored and external entities are never resolved, which also means no XXE fetch is attempted.
  • Element order survives as JSON key order, which is preserved in practice but is not guaranteed by the JSON specification.
  • Whitespace-only text between elements is trimmed away, so pretty-printed XML does not produce empty string values.

Use cases

  • Consuming a SOAP response in JavaScript. Convert the envelope to JSON so it can be read with ordinary property access, with namespace stripping to shorten the keys.
  • Reading an RSS or Atom feed. Both are XML; array forcing matters here because a feed with one item would otherwise change shape.
  • Migrating a legacy config. An XML settings file becomes JSON for a modern loader.
  • Working with a sitemap. <url> entries become an array you can iterate.
  • Understanding an unfamiliar document. The JSON view is often easier to scan than deeply nested XML, and the depth and element counts give you its shape at a glance.
  • Feeding a JSON-only pipeline. Many log processors, schema validators and query tools accept JSON only.

The reverse direction is JSON to XML, which faces the mirror-image problems. For flat output, XML to Properties is often a better fit, and the XML Validator and XML Formatter handle checking and tidying. To query the result, use the JSONPath Tester.

Privacy

Validation and parsing run entirely in your browser. The XML is not uploaded, and no external entity or DTD is fetched. Usage analytics record only the attribute convention, whether array forcing was on, and whether the document had attributes.

Frequently Asked Questions

Why does my JSON shape change depending on the XML document?

Because XML has no array type. One child produces an object and two produce an array, even though both documents are valid against the same schema. Enable array forcing for a stable shape, or normalise with [].concat(value ?? []) in your own code.

What is the #text key for?

It holds an element's own text when that element also has attributes or children. Title must become an object to carry the id, and an object cannot also be a string, so the text goes under a reserved key. You can change which key is used.

Should I strip namespace prefixes?

It gives much cleaner keys and is usually fine for a single-namespace document. Avoid it when two namespaces share a local name — common in SOAP — because stripping merges them and one value silently overwrites the other.

Why are numbers coming through as strings?

Because XML values are always character data, and only an XSD says otherwise. Conversion is off by default because it loses leading and trailing zeros: 0123 becomes 123 and 1.0 becomes 1. Turn it on if you know your data has no zero-padded codes.

What happens to CDATA?

It is unwrapped to its text content, so becomes the string "5 < 10". JSON has no equivalent marker, so if you convert back to XML those characters will be escaped as entities instead. The value is identical; only the notation differs.

Can I convert the JSON back to the original XML?

Approximately, never exactly. Comments, CDATA markers, the declaration, processing instructions and the attribute-versus-element distinction for unprefixed keys are all lost. If a round trip matters, keep the XML as the source of truth and treat the JSON as a derived view.

Is an empty element null or an empty string?

An empty string. becomes "empty": "" rather than null, because XML does not distinguish an empty element from a null one — that distinction simply does not exist in the source, so inventing it would be wrong.