← All articles

XML to JSON: Attributes, Arrays & Namespaces

Convert XML to JSON without losing shape: attributes, text nodes, repeated elements, namespaces, CDATA, entities, type coercion, and code examples.

Converting XML to JSON is not just replacing angle brackets with braces. XML has elements, attributes, namespaces, text nodes, CDATA sections, comments, processing instructions, entity references, and repeated child elements. JSON has objects, arrays, strings, numbers, booleans, and null. There is no official one-to-one mapping between the two.

The useful way to think about XML to JSON conversion is this: choose a convention, document it, and apply it consistently. For data-oriented XML, a predictable mapping works well. For document-style XML with mixed prose and markup, conversion can be lossy unless you preserve node order explicitly.

This guide uses the same practical convention as the converter on this site: the document root becomes the top-level key, attributes become @-prefixed keys, element text goes under #text when needed, and repeated sibling elements become arrays.

XML to JSON in One Minute

XML feature JSON mapping used here Why it matters
Root element Top-level object key JSON needs a place to put the XML document element
Attribute @id, @currency, @xmlns:soap JSON has no attribute syntax
Text-only element Plain string Keeps simple elements simple
Attribute plus text Object with @... and #text Attributes and text both need a home
Repeated child elements Array XML represents lists as repeated sibling tags
Namespace prefix Keep prefix in key, such as soap:Envelope Avoids collisions and preserves round-trip information
CDATA Text CDATA is a syntax wrapper, not a separate JSON type
Entity reference Decoded character & becomes & after parsing

Here is the shape:

<order id="ord_1001" status="paid">
  <customer>Ada</customer>
  <total currency="USD">19.99</total>
  <item sku="mug-white">Coffee mug</item>
  <item sku="book-json">JSON guide</item>
</order>
{
  "order": {
    "@id": "ord_1001",
    "@status": "paid",
    "customer": "Ada",
    "total": {
      "@currency": "USD",
      "#text": "19.99"
    },
    "item": [
      {
        "@sku": "mug-white",
        "#text": "Coffee mug"
      },
      {
        "@sku": "book-json",
        "#text": "JSON guide"
      }
    ]
  }
}

Notice the deliberate choice: 19.99 stays a string. XML text is text. If you want numbers, booleans, or dates, do that as a second, explicit data-modeling step.

First Decide: Data XML or Document XML?

XML used for data exchange is usually a good conversion target:

<invoice id="inv_7">
  <amount currency="USD">1299</amount>
  <paid>true</paid>
</invoice>

The structure is field-like. JSON can represent it cleanly.

Document-style XML is different:

<p>Hello <strong>Ada</strong>, welcome back.</p>

A simple object mapping has trouble preserving text order:

{
  "p": {
    "#text": "Hello , welcome back.",
    "strong": "Ada"
  }
}

That JSON lost the exact node sequence. If order matters, use an AST-like representation:

{
  "name": "p",
  "children": [
    { "type": "text", "value": "Hello " },
    { "type": "element", "name": "strong", "children": [{ "type": "text", "value": "Ada" }] },
    { "type": "text", "value": ", welcome back." }
  ]
}

For SOAP payloads, RSS items, sitemaps, product feeds, invoices, and config files, the compact @ / #text convention is usually enough. For XHTML, DocBook, WordprocessingML, or anything where prose order matters, do not flatten too aggressively.

The Root Element Becomes the Top-Level Key

XML has exactly one document element. JSON can start with any value, but XML-to-JSON converters normally preserve the XML root name:

<catalog>
  <book id="bk101">
    <title>XML Developer's Guide</title>
  </book>
</catalog>
{
  "catalog": {
    "book": {
      "@id": "bk101",
      "title": "XML Developer's Guide"
    }
  }
}

Keeping the root matters when you convert back to XML. Without it, a converter has to invent a generic <root> wrapper.

Attributes Become @ Keys

XML attributes are metadata on an element:

<book id="bk101" lang="en" />

JSON has no attribute slot, so this guide maps each attribute to an @-prefixed key:

{
  "book": {
    "@id": "bk101",
    "@lang": "en"
  }
}

Other tools may use an @attributes object, a $ prefix, or a _attributes property. Those can all work. The important part is consistency. Downstream code needs to know whether the book ID lives at:

book["@id"]
book["@attributes"].id
book.$.id

If you control the mapping, @id is compact and reversible. If you are integrating with an existing system, use its convention instead of translating twice.

Text Nodes: String or #text

When an element only contains text, the simplest JSON is a string:

<title>Effective TypeScript</title>
{
  "title": "Effective TypeScript"
}

When the element has attributes and text, the text needs a separate key:

<price currency="USD">9.99</price>
{
  "price": {
    "@currency": "USD",
    "#text": "9.99"
  }
}

That is why #text exists. Without it, there is no reliable place to store both currency="USD" and the text value 9.99.

Whitespace is another decision. Many data converters trim whitespace-only indentation. That is fine for data XML. It is not fine for XML where whitespace is meaningful.

Repeated Elements Become Arrays

XML represents lists by repeating sibling elements:

<tags>
  <tag>json</tag>
  <tag>xml</tag>
  <tag>converter</tag>
</tags>
{
  "tags": {
    "tag": ["json", "xml", "converter"]
  }
}

The annoying edge case is the single item:

<tags>
  <tag>json</tag>
</tags>

Many converters output:

{
  "tags": {
    "tag": "json"
  }
}

That means your JSON shape changes depending on the input. Code that expects tags.tag.map(...) works with three tags and crashes with one tag.

There are two sane fixes:

  1. Configure the parser to always array-ify known repeated elements.
  2. Normalize after parsing.

In JavaScript:

function asArray(value) {
  if (value === undefined || value === null) return [];
  return Array.isArray(value) ? value : [value];
}

const tags = asArray(result.tags?.tag);

For production contracts, write down which fields are always arrays. Do not let consumers infer list-ness from whatever sample happened to have two items.

Namespaces: Keep Prefixes Unless You Have a Better Plan

XML namespaces qualify names with URI-bound prefixes. The W3C Namespaces in XML recommendation defines this mechanism so different vocabularies can use the same local names without colliding.

Example:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <m:GetPrice xmlns:m="https://example.com/stock">
      <m:Item>ABC</m:Item>
    </m:GetPrice>
  </soap:Body>
</soap:Envelope>

A conservative JSON mapping keeps the prefixes and namespace declarations:

{
  "soap:Envelope": {
    "@xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/",
    "soap:Body": {
      "m:GetPrice": {
        "@xmlns:m": "https://example.com/stock",
        "m:Item": "ABC"
      }
    }
  }
}

This is not the prettiest JSON, but it avoids losing information.

Stripping prefixes can be dangerous:

soap:Body -> Body
m:Body -> Body

Those two names may mean different things. If you remove the prefix, you risk overwriting or merging unrelated elements.

One subtle namespace point: prefixes are aliases, not the namespace identity. Two XML documents can use different prefixes for the same namespace URI. If namespace semantics are critical, store expanded names or preserve the namespace URI mapping rather than trusting the prefix alone.

Entities, CDATA, Comments, and Processing Instructions

A real XML parser resolves entity references before your converter sees text content:

<message>Hello &amp; welcome</message>
{
  "message": "Hello & welcome"
}

CDATA sections also become text:

<script><![CDATA[if (a < b) alert("ok");]]></script>
{
  "script": "if (a < b) alert(\"ok\");"
}

Comments and processing instructions usually do not belong in data JSON:

<?xml version="1.0"?>
<!-- generated by partner feed -->
<item id="1">Coffee</item>

Most data converters skip the declaration and comment, returning just:

{
  "item": {
    "@id": "1",
    "#text": "Coffee"
  }
}

If comments, processing instructions, or exact entity spelling matter, you need a representation closer to an XML DOM, not a compact data object.

Type Coercion: XML Text Is Text

XML does not have JSON's native number, boolean, array, object, and null value types. Element text and attribute values arrive as strings unless a schema or parser option coerces them.

This XML:

<product id="007">
  <price>19.99</price>
  <active>false</active>
  <created>2026-05-26T10:30:00Z</created>
</product>

Should not be blindly converted to:

{
  "product": {
    "@id": 7,
    "price": 19.99,
    "active": false,
    "created": "2026-05-26T10:30:00Z"
  }
}

The ID changed from "007" to 7. That can break account IDs, SKUs, postal codes, invoice numbers, and anything with leading zeros.

A safer first-pass conversion keeps strings:

{
  "product": {
    "@id": "007",
    "price": "19.99",
    "active": "false",
    "created": "2026-05-26T10:30:00Z"
  }
}

Then your application can coerce only the fields it understands:

const product = converted.product;

const model = {
  id: product["@id"],
  price: Number(product.price),
  active: product.active === "true",
  created: product.created
};

This extra step is boring, but it prevents accidental data loss.

A Practical Conversion Checklist

Before you convert XML to JSON for an integration, decide these rules:

Decision Conservative default
Root element Preserve it as the top-level key
Attributes Prefix with @
Text with attributes Store as #text
Repeated elements Force known repeatable names to arrays
Namespaces Preserve prefixes and xmlns declarations
Mixed content Use an ordered node representation if text order matters
Comments and processing instructions Drop for data XML; preserve only when required
DTDs and custom entities Disable or avoid in untrusted input
Type coercion Keep strings first, then coerce known fields
Round-trip back to XML Test JSON -> XML -> JSON with real samples

The best XML-to-JSON conversion is not the shortest JSON. It is the JSON shape your consumers can rely on.

JavaScript Example with fast-xml-parser

For application code, a library is usually better than a hand-rolled parser. With fast-xml-parser, keep attributes and choose a prefix that matches your contract:

npm install fast-xml-parser
const { XMLParser } = require("fast-xml-parser");

const parser = new XMLParser({
  ignoreAttributes: false,
  attributeNamePrefix: "@",
  textNodeName: "#text"
});

const xml = `<price currency="USD">9.99</price>`;
const json = parser.parse(xml);

console.log(JSON.stringify(json, null, 2));

Expected shape:

{
  "price": {
    "#text": "9.99",
    "@currency": "USD"
  }
}

Parser options vary. Check attribute prefix, text node name, array behavior, namespace handling, and type coercion before you treat the output as a stable API contract.

Browser Example with DOMParser

Browsers include DOMParser, which can parse XML into a DOM Document. The small walker below follows the @ / #text convention:

function elementToJson(element) {
  const output = {};

  for (const attr of element.attributes) {
    output[`@${attr.name}`] = attr.value;
  }

  for (const child of element.childNodes) {
    if (child.nodeType === Node.TEXT_NODE) {
      const text = child.nodeValue.trim();
      if (text) output["#text"] = (output["#text"] || "") + text;
      continue;
    }

    if (child.nodeType !== Node.ELEMENT_NODE) continue;

    const value = elementToJson(child);
    const existing = output[child.nodeName];

    if (existing === undefined) {
      output[child.nodeName] = value;
    } else if (Array.isArray(existing)) {
      existing.push(value);
    } else {
      output[child.nodeName] = [existing, value];
    }
  }

  const keys = Object.keys(output);
  if (keys.length === 1 && keys[0] === "#text") return output["#text"];
  return output;
}

function xmlToJson(xml) {
  const doc = new DOMParser().parseFromString(xml, "application/xml");
  const error = doc.querySelector("parsererror");

  if (error) {
    throw new Error(error.textContent || "Invalid XML");
  }

  return {
    [doc.documentElement.nodeName]: elementToJson(doc.documentElement)
  };
}

This is useful for understanding the mapping. For production, use a maintained parser and test with real partner feeds.

Python Example with xmltodict

xmltodict uses familiar conventions: attributes are prefixed with @, and text content uses #text.

pip install xmltodict
import json
import xmltodict

xml = '<price currency="USD">9.99</price>'
data = xmltodict.parse(xml)

print(json.dumps(data, indent=2))

Expected output:

{
  "price": {
    "@currency": "USD",
    "#text": "9.99"
  }
}

For untrusted XML, follow your platform's XML security guidance. DTDs, external entities, and entity expansion are not just conversion details; they can become security and availability issues.

Convert XML to JSON Online

Use the JSON to XML converter when you need a quick browser-local conversion in either direction:

  1. Paste XML.
  2. Click To JSON.
  3. Check the root key, attributes, repeated elements, namespaces, and text nodes.
  4. Normalize arrays or field types if your downstream code needs a stricter shape.
  5. Copy the JSON, view it in JSON Viewer, or compare changes with JSON Diff.

The converter uses the same broad conventions described here: @ for attributes, #text for text where needed, and arrays for repeated sibling elements. The conversion runs in your browser.

When Not to Convert XML to JSON

Do not convert XML just because JSON is easier to read.

Keep XML when:

  • The receiver requires XML.
  • You must preserve comments, processing instructions, entity spelling, or exact node order.
  • The document is markup-heavy rather than data-heavy.
  • You need XSD validation or XML signatures.
  • Namespace semantics must remain exact and your JSON mapping does not preserve them.

Convert to JSON when:

  • The XML is data-oriented.
  • Your app already uses JSON internally.
  • You need to inspect or transform values with JSON tooling.
  • The receiver accepts the mapped JSON contract.

Frequently Asked Questions

How do I convert XML to JSON?

Parse the XML, preserve the root element as the top-level JSON key, map attributes to @ keys, put text in #text when an element also has attributes or child elements, and turn repeated sibling elements into arrays.

How are XML attributes represented in JSON?

By convention, attributes become keys prefixed with @, such as @id or @currency. Some converters use an @attributes object or a different prefix, so pick one convention and keep it consistent.

What happens to text inside an XML element?

If the element contains only text, it can become a plain string. If the element also has attributes or child elements, the text needs a reserved key such as #text.

Why does the same XML element sometimes become an object and sometimes an array?

Many converters emit a single value when an element appears once and an array when it appears multiple times. For stable code, configure known repeatable elements as arrays or normalize values after parsing.

How should XML namespaces be handled in JSON?

The safest default is to keep namespace prefixes in JSON keys and preserve xmlns declarations as attributes. Stripping prefixes makes cleaner keys, but it can merge elements from different namespaces.

Does XML to JSON preserve numbers, booleans, and dates?

Not automatically. XML text and attribute values are strings unless a parser or application layer coerces them. Keep strings on the first pass, then convert only fields whose meaning you know.

What happens to CDATA and XML entities?

CDATA becomes text, and predefined entities such as &amp; are decoded to characters during XML parsing. If exact CDATA boundaries or entity spelling matter, a compact JSON object is not enough.

Can XML to JSON conversion be lossless?

It can be close for data-oriented XML if you preserve attributes, text, arrays, namespaces, and root names. It is often lossy for document-style XML with mixed content, comments, processing instructions, DTDs, or exact whitespace requirements.

Related Tools & Guides

Sources

Last reviewed July 2026.