Skip to content

XML to JSON Converter

Paste XML, get JSON instantly. Converts attributes to @_ keys, handles repeated elements as arrays. 100% in-browser, nothing uploaded, no signup.

No Tracking Runs in Browser Free
Options · 2 spaces · XML → JSON
Indent
0 chars
JSON Output
0 lines
Reviewed for XML 1.0 well-formedness handling, @_/#text convention correctness, and string-preservation accuracy — Go Tools Engineering Team · May 29, 2026

What is XML-to-JSON Conversion and How Does It Work?

XML (Extensible Markup Language) and JSON (JavaScript Object Notation) are both structured data formats, but they have fundamentally different models: XML is a tree of elements with attributes and mixed content (text interleaved with child elements); JSON is a tree of objects, arrays, strings, numbers, booleans, and null values. Converting between them requires a set of conventions to bridge the mismatch.

This tool uses the most widely adopted convention, the same one used by popular libraries like fast-xml-parser (Node.js), xmltodict (Python), and JAXB (Java):

**1. Attributes → @_ prefix.** XML attributes have no direct JSON equivalent. The convention is to represent them as keys prefixed with @_. So becomes { "@_id": "42", "@_role": "admin" } inside the user object. This prefix is unambiguous: no valid XML element name starts with @, so there is no collision with child element names.

**2. Element text content with attributes → #text.** When an element has both attributes and text content — 29.99 — the text must share the same JSON object as the attributes. The convention is to store it under the key #text, producing { "@_currency": "USD", "#text": "29.99" }. Elements with only text content and no attributes convert to a plain string value.

**3. Repeated sibling elements → arrays.** XML allows multiple child elements with the same name; JSON objects cannot have duplicate keys. The solution is to collect same-named siblings into an array. One child becomes a single object; two or more children become an array of objects. This is the most important behavioral detail to understand: the JSON shape changes based on how many siblings exist in the XML.

**4. No type coercion — all values stay strings.** XML has no native type system for text content. A value of "123" in XML is a string. Converting it to the JSON number 123 requires making an assumption about the author's intent — an assumption that is wrong for ZIP codes ("01234" → 1234), phone numbers, padded identifiers, and precision-sensitive decimal strings. This tool preserves all values as strings. Apply type coercion in your own code for the fields where you know the type.

**5. Lossy for comments, processing instructions, and namespaces.** XML supports features that JSON does not: comments (), processing instructions (), and namespace semantics. These are discarded or approximated during conversion. For lossless XML work — reformatting, minifying, validating — use the XML Formatter instead. For the reverse conversion — building XML from JSON — use the JSON to XML Converter.

**Why convert XML to JSON at all?** JSON is the native format of JavaScript and the default interchange format for REST APIs. If you receive XML from a legacy SOAP service, an RSS feed, a sitemap, or an enterprise system, converting it to JSON lets you work with the data using standard JavaScript object access, JSON path queries, and any JSON-aware database or API. The conversion is a one-way bridge: useful for consuming XML data in a modern stack, but not for preserving or round-tripping XML documents.

// Convert XML to JSON in Node.js using fast-xml-parser
import { XMLParser } from 'fast-xml-parser';

const xml = `<catalog>
  <product id="P01">
    <name>Wireless Headphones</name>
    <price currency="USD">79.99</price>
  </product>
</catalog>`;

const parser = new XMLParser({
  ignoreAttributes: false,      // preserve attributes
  attributeNamePrefix: '@_',    // @_ prefix for attributes
  textNodeName: '#text',        // #text for mixed element content
  parseAttributeValue: false,   // no type coercion on attributes
  parseTagValue: false,         // no type coercion on element text
});

const result = parser.parse(xml);
console.log(JSON.stringify(result, null, 2));
// {
//   "catalog": {
//     "product": {
//       "@_id": "P01",
//       "name": "Wireless Headphones",
//       "price": {
//         "@_currency": "USD",
//         "#text": "79.99"
//       }
//     }
//   }
// }

Key Features

Live Conversion

JSON output updates instantly as you type or paste XML — no Convert button needed. Large inputs (>200KB) automatically switch to manual mode to keep the browser responsive.

@_ Attribute Prefix Convention

XML attributes become @_-prefixed JSON keys, following the fast-xml-parser and xmltodict convention. becomes { "@_attr": "val" } — unambiguous and collision-free with any valid element name.

Array Auto-Detection for Repeated Elements

Same-named sibling elements automatically become a JSON array. One → object; two or more elements → array. No configuration needed — the tool detects repeats automatically.

No Type Coercion — Values Stay Strings

All XML values are preserved as JSON strings. Leading zeros, phone numbers, padded codes, and boolean-looking strings like "true" or "false" are never silently converted to numbers or booleans.

100% Browser-Based Privacy

All conversion runs locally in your browser using JavaScript. Your XML — including credentials, internal configs, and sensitive payloads — is never sent to any server, never logged, and never stored.

Well-Formedness Error Reporting

If your XML is not well-formed, the tool reports the exact line number and column number of the first error, so you can locate and fix the problem immediately before converting.

Examples

Config File

<?xml version="1.0" encoding="UTF-8"?>
<appConfig version="2.1" env="production">
  <database>
    <host>db.example.com</host>
    <port>5432</port>
    <name>myapp_prod</name>
    <pool max="10" min="2"/>
  </database>
  <cache enabled="true">
    <ttl>3600</ttl>
    <driver>redis</driver>
  </cache>
</appConfig>

A small application config with attributes and nested elements. Attributes become @_-prefixed keys in the JSON output: version="2.1" becomes "@_version": "2.1" under appConfig, and the pool element's max and min attributes become "@_max": "10" and "@_min": "2". All values — including port and ttl — remain strings, not numbers, because the converter performs no type coercion.

Sitemap Fragment

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://go-tools.org/tools/xml-to-json</loc>
    <lastmod>2026-05-29</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://go-tools.org/tools/json-to-xml</loc>
    <lastmod>2026-05-29</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
  </url>
</urlset>

A two-URL sitemap.xml fragment. Because there are two sibling elements under , they become a JSON array. The xmlns namespace declaration becomes "@_xmlns" on the urlset object. The priority value "0.8" stays a string — no numeric coercion — so leading-zero values like "0.08" would also be preserved exactly.

RSS Item

<item>
  <title>XML to JSON: A Practical Guide</title>
  <link>https://go-tools.org/blog/xml-to-json-guide</link>
  <pubDate>Thu, 29 May 2026 00:00:00 GMT</pubDate>
  <category>Developer Tools</category>
  <description>Learn how XML attributes map to @_ keys, how repeated elements become arrays, and why values stay strings in any XML-to-JSON conversion.</description>
  <guid isPermaLink="true">https://go-tools.org/blog/xml-to-json-guide</guid>
</item>

An RSS 2.0 item element. The guid element has an isPermaLink attribute, so the JSON output for guid becomes an object with "@_isPermaLink": "true" and "#text": "https://go-tools.org/blog/xml-to-json-guide" — the attribute and the element's text content coexist as sibling keys. All other elements have only text content and convert directly to string values.

How to Use

  1. 1

    Paste Your XML

    Enter or paste your XML into the input field above. You can also click 'Load example' to try a sample — a config file, a sitemap fragment, or an RSS item.

  2. 2

    See Live JSON Output

    JSON appears instantly in the output panel. Attributes become @_-prefixed keys, text content of mixed elements becomes #text, and repeated sibling elements become arrays. Adjust indent (2 or 4 spaces) as needed.

  3. 3

    Copy or Download

    Click Copy to grab the JSON to your clipboard, or Download to save it as a .json file ready for your application, API, or data pipeline.

Common Conversion Pitfalls

Forgetting That Values Stay Strings

The converter performs no type coercion. If your downstream code expects JSON numbers but the XML contains numeric strings, you must coerce them explicitly after converting. Relying on implicit string-to-number coercion (JavaScript's == or arithmetic operators) is error-prone — always use parseInt(), parseFloat(), or Number() deliberately.

✗ Wrong
// XML: <timeout>30</timeout>
// JSON output: { "timeout": "30" }  ← string, not number
// Dangerous: comparing as-is
if (config.timeout > 25) { ... }  // "30" > 25 coerces, but fragile
✓ Correct
// XML: <timeout>30</timeout>
// JSON output: { "timeout": "30" }
// Safe: explicit coercion where you know the type
if (parseInt(config.timeout, 10) > 25) { ... }

Single Item vs. Array Shape Mismatch

When there is only one element, the output is an object; when there are two or more, it becomes an array. Consumer code that always does result.items.forEach() will crash when items is a single object. Always normalize to an array after converting if your schema allows one-or-many.

✗ Wrong
// XML with one item: <root><item>a</item></root>
// JSON: { "root": { "item": "a" } }  ← string, not array
result.root.item.forEach(i => console.log(i));  // TypeError: not a function
✓ Correct
// Normalize to array regardless of count
const items = [].concat(result.root?.item ?? []);
items.forEach(i => console.log(i));  // safe for 0, 1, or many

Mixed Content: Text and #text Key

When an element has both attributes and text content, the text goes under #text. Accessing the text value as if it were a plain string will return an object instead. Always check whether an element value is a string or an object with a #text key when attributes are possible.

✗ Wrong
// XML: <price currency="USD">29.99</price>
// JSON: { "price": { "@_currency": "USD", "#text": "29.99" } }
const amount = result.price;  // { "@_currency": "USD", "#text": "29.99" }, not "29.99"
✓ Correct
// Handle both plain string and @_/#text object
const raw = result.price;
const amount = typeof raw === 'object' ? raw['#text'] : raw;  // "29.99"

Not-Well-Formed XML Input

The converter requires well-formed XML. Common causes of well-formedness errors: unescaped & in text content (use &), mismatched tags ( vs — XML is case-sensitive), multiple root elements, or unclosed tags. The error message shows the line and column of the first problem.

✗ Wrong
<!-- Unescaped & — well-formedness error -->
<query>name = 'Alice' & role = 'admin'</query>
✓ Correct
<!-- Escaped & — valid XML -->
<query>name = 'Alice' &amp; role = 'admin'</query>

Leading-Zero Values Corrupted by Coercion

This tool preserves leading zeros because it performs no type coercion. If you post-process the JSON with a library that does coerce (some JSON schema validators, ORM mappers), the leading zero may be lost. Always declare the field type explicitly as string in your schema.

✗ Wrong
// XML: <zipCode>01234</zipCode>
// This tool outputs: { "zipCode": "01234" }  ← correct
// But if a downstream schema coerces:
// { zipCode: z.number() }  → 1234  ← leading zero lost
✓ Correct
// Declare ZIP codes and other padded identifiers as strings
// { zipCode: z.string() }  → "01234"  ← correct

Common Use Cases

Consuming SOAP / Legacy XML APIs
Legacy enterprise services (banking, insurance, logistics) often expose SOAP APIs that return XML. Convert the response to JSON to work with the data using modern JavaScript tooling, store it in a document database, or forward it to a REST API consumer.
Processing RSS and Atom Feeds
RSS 2.0 and Atom 1.0 feeds are XML. Convert feed XML to JSON to extract titles, links, publication dates, and descriptions for feed aggregators, content pipelines, and notification systems without an XML parsing library dependency.
Parsing Sitemap Files
sitemap.xml files enumerate a site's URLs with metadata. Convert a sitemap to JSON to analyze URL coverage, build crawl queues, compare sitemaps across environments, or feed URLs into a link checker or SEO audit tool.
Transforming Configuration Files
Many enterprise systems (Spring, Maven, Ant, JBoss, Tomcat) use XML configuration files. Convert them to JSON for analysis, migration scripting, documentation generation, or importing into tools that speak JSON natively.
Mobile and Android Development
Android resources, layout files, and AndroidManifest.xml are all XML. Convert them to JSON for analysis scripts, automated auditing, or generating documentation about declared permissions, activities, and resource values.
Data Migration and ETL
Data exports from enterprise systems (ERP, CRM, payroll) often arrive as XML. Convert to JSON as the first step in an ETL pipeline to load the data into a JSON-native data store like MongoDB, Firestore, or a REST API.

Technical Details

Browser DOMParser for XML Parsing
XML input is parsed using the browser's native DOMParser with the 'text/xml' MIME type — the same engine used to render SVG and XHTML. It is fully XML 1.0 compliant and reports well-formedness errors with line and column information. This approach handles namespaces, CDATA sections, processing instructions, and entity references correctly without any third-party dependency.
Recursive DOM-to-JSON Serialization
After parsing, the tool walks the DOM tree recursively. Element nodes become JSON objects; their attributes become @_-prefixed keys; text-only elements become plain string values; elements with both attributes and text content gain a #text key for the text content. Same-named sibling elements are collected and output as a JSON array. Whitespace-only text nodes between elements are discarded.
No Type Coercion — String Preservation
All attribute values and text content are written to the JSON output as strings. No parseInt, parseFloat, or boolean parsing is applied. This is a deliberate design choice to prevent silent data corruption for leading-zero values, precision-sensitive decimals, and boolean-looking strings. Type coercion, where needed, should be applied explicitly in downstream code.
100% Browser-Based — No Upload, No Server
All processing runs in your browser's JavaScript engine. No data is transmitted over the network at any point. Inputs larger than 200KB automatically switch from live mode to manual mode (requiring an explicit Convert click) to keep the browser responsive and prevent main-thread blocking during heavy DOM traversal.

Best Practices

Validate XML Well-Formedness First
If your XML source is hand-written or generated by a system that occasionally produces malformed output, validate it before converting. Use the XML Formatter's Validate button to confirm the XML is well-formed and get precise error locations for any problems.
Normalize One-or-Many Fields to Arrays
When your XML schema allows either one or many child elements with the same name, the JSON output will be an object for one element and an array for many. Always normalize these fields in your consumer code: const items = [].concat(result.items ?? []) — this is safe for zero, one, or many and prevents TypeErrors.
Apply Type Coercion Explicitly and Selectively
Because this tool preserves all values as strings, apply type coercion deliberately in your code for the specific fields that need it. Using parseInt(val, 10) for a known integer field is safe. Applying coercion broadly with Number(val) risks corrupting leading-zero values, empty strings, and precision-sensitive decimals.
Check for #text When Accessing Element Values
If an element can have attributes in some payloads and not in others, its JSON value will be either a plain string (no attributes) or an object with @_ keys and a #text key (with attributes). Write robust accessors: const val = typeof node === 'object' ? node['#text'] : node. This pattern handles both shapes and avoids silent undefined values.
For Lossless XML Work, Use the XML Formatter
XML-to-JSON conversion discards comments, processing instructions, and namespace semantics. If you need to preserve all XML content exactly, use the XML Formatter to beautify, minify, and validate without any loss. Convert to JSON only when you need to work with the data in a JSON-native context.

Frequently Asked Questions

Is my XML data sent to a server when I use this tool?
No. All conversion happens entirely inside your browser using JavaScript. Your XML is never transmitted over the network, never stored on any server, and never logged or analyzed. This makes the tool safe to use with XML payloads containing API credentials, internal service configuration, SOAP WS-Security tokens, healthcare HL7/FHIR data, or any other sensitive content. You can verify this by opening your browser's Network tab — you will see zero requests triggered by pasting or converting XML.
How do XML attributes map in the JSON output?
XML attributes become JSON keys prefixed with @_. For example, produces a JSON object containing "@_id": "P01" and "@_category": "electronics" alongside any child element keys. When an element has both attributes and text content — such as 29.99 — the text content is stored under the special key "#text", so the result is { "@_currency": "USD", "#text": "29.99" }. This convention is consistent and predictable: @_ always means attribute, #text always means element text content.
Does the converter coerce numbers or booleans?
No. All XML text content and attribute values become JSON strings, regardless of how they look. 42 becomes "count": "42", not 42. true becomes "enabled": "true", not true. This is intentional and important: it preserves leading zeros (phone numbers, account codes, ZIP codes like "01234"), numeric precision for values like "0.100", and string values that happen to look boolean. If you need numbers or booleans in your downstream JSON, apply type coercion in your own code after converting — where you control exactly which fields get coerced.
How are repeated (same-named sibling) elements handled?
A single child element becomes a JSON object. Two or more child elements with the same tag name under the same parent become a JSON array. For example, a produces { "root": { "item": "a" } } — item is an object (string). But ab produces { "root": { "item": ["a", "b"] } } — item is an array. This means the structure of your JSON output depends on the number of sibling elements in the XML, which is one reason XML-to-JSON conversion is convention-based. If your XML schema can have either one or many items, your consumer code must handle both the object and array cases.
Is XML-to-JSON conversion lossless?
No. XML has features that have no JSON equivalent and are dropped during conversion: XML comments () are discarded, processing instructions () are discarded, namespace prefix bindings are partially preserved as @_ attributes but their semantics are not, and the relative order of mixed-content nodes (text interleaved with child elements) may not round-trip perfectly. For purely structural XML without comments or processing instructions, the conversion preserves all element names, attribute names, attribute values, and text content. For lossless XML work — formatting, validating, or inspecting XML without any data loss — use the XML Formatter instead.
How do I convert JSON back to XML?
Use our companion JSON to XML Converter. It applies the same conventions in reverse: @_-prefixed keys become XML attributes, #text keys become element text content, and JSON arrays become repeated same-named sibling elements. This makes the two tools symmetric for round-trip use cases.
What happens to XML namespaces?
Namespace declarations (xmlns="..." and xmlns:prefix="...") are treated as regular attributes and appear in the JSON output as @_xmlns and @_xmlns:prefix keys. The namespace prefix in element names is preserved as part of the element name key (e.g., becomes "soap:Body" in the JSON). The semantic meaning of namespaces — that two prefixes might point to the same URI — is not interpreted. If precise namespace handling matters for your use case, parse the XML in a namespace-aware parser rather than converting to JSON.
Why does 0123 become "0123" and not 123?
Because the converter performs no type coercion. The string "0123" and the number 123 are different values: "0123" has a leading zero that is meaningful in many contexts (account codes, postal codes, national identification numbers, padded identifiers). Silently dropping that leading zero would corrupt data. The safe default is to preserve all values as strings exactly as they appear in the XML. Apply numeric parsing selectively in your own code for the specific fields where you know the value is always a plain integer.
What is the difference between this tool and an XML formatter?
The XML Formatter reformats XML — it changes indentation and whitespace but the output is still XML. This XML-to-JSON Converter changes the format entirely: the output is a JSON document that represents the XML structure using the @_ attribute convention. Use the formatter when you want to read, edit, validate, or minify XML. Use this converter when you need to work with the XML data in a JavaScript application, feed it into a REST API, or store it in a JSON document store.
Is there a file size limit?
There is no hard limit, but inputs larger than 200KB automatically switch from live conversion to manual mode. In manual mode, a Convert button appears and conversion runs only when you click it — this keeps the browser responsive while parsing large XML documents. For very large XML files (multi-megabyte data exports), consider command-line tools for better performance: python3 -c "import sys, xmltodict, json; print(json.dumps(xmltodict.parse(sys.stdin.read()), indent=2))" or node -e with a dedicated XML-to-JSON library.
Does the converter handle CDATA sections?
Yes. CDATA section content () is treated as element text content and appears as a plain string value in the JSON output. The CDATA delimiters themselves are stripped — only the content inside is preserved. For example, produces "note": "if (a < b) return;" in JSON. This is the correct behavior: CDATA is just a way to embed text with special characters without escaping them; the semantic meaning is the text content.
Can I convert XML with multiple root elements?
No. XML with multiple root elements is not well-formed, and this tool requires well-formed XML input. If your XML parser gives you multiple root elements (common when stitching together XML fragments), wrap them in a single root element before converting. For example, if you have , convert it as . The error message will indicate the position of the well-formedness problem so you can fix it quickly.

Related Tools

View all tools →