
XML ↔ JSON Converter
Convert XML to JSON or JSON to XML instantly. Handles attributes, text content, and repeated elements. 100% client-side.
Last reviewed: April 2026New to this tool? Click here for instructions
How to Use the XML to JSON Converter
To use the XML to JSON converter, follow these steps:
1. Choose the direction of conversion (XML to JSON or JSON to XML) using the chips above the input area.
2. Paste your XML or JSON data into the input area on the left.
3. View the converted output on the right, which updates as you type.
4. Copy or download the result as a .json or .xml file.
When to Use the Tool in Real Workflows
Use the XML to JSON converter when you need to convert data between these two formats for various purposes such as SOAP to REST migration, RSS/Atom feed parsing, configuration files, and data transformation pipelines.
How It Works
The converter uses the BadgerFish-inspired convention to map XML to JSON. This means that XML attributes become JSON properties prefixed with @, mixed content uses #text for the text portion, repeated sibling elements are grouped into a JSON array, and text-only elements with no attributes become plain string values. Nested elements become nested JSON objects.
Tips, Edge Cases, or Limitations
The converter handles attributes, text content, repeated elements, and nested structures effectively. However, it cannot perfectly preserve all XML features. XML namespaces, processing instructions, CDATA sections, and XML comments are not preserved in the JSON output. The root element name is preserved as the top-level key in the JSON object.
When converting JSON back to XML, properties with @ prefix become attributes, and arrays generate repeated elements with the same tag name. The root element is named "root" by default if the JSON starts with an object.
Frequently Asked Questions
Convert XML to JSON (and back) instantly in your browser — attributes, namespaces, CDATA, and repeated elements handled correctly. No file uploads, no server round-trips, no account required. The tool follows the @-prefix attribute convention used by xml2js and xml-js, so output drops directly into Node.js codebases without glue code.
What This Tool Does
The converter handles bidirectional transformation between XML and JSON. Paste XML on the left and get formatted JSON on the right, or reverse the direction to reconstruct XML from a JSON structure. Both directions run entirely inside your browser using the native DOMParser and XMLSerializer APIs — your data never leaves the tab.
Bidirectional Conversion: XML→JSON and JSON→XML
XML→JSON mode parses the full document tree and emits a JSON object. JSON→XML mode reads the same @-prefixed structure and rebuilds a well-formed XML document, re-adding a <?xml version="1.0" encoding="UTF-8"?> declaration automatically. Round-trip fidelity is high for elements, attributes, and text content; XML comments and processing instructions are lossy (see the CDATA and comments section below).
@prefix Attribute Convention (xml2js / xml-js Compatible)
Multiple valid XML→JSON serialization approaches exist. This tool uses the @-prefix convention: every XML attribute becomes a JSON key prefixed with @. For example, <order id="ORD-2847"> produces "@id": "ORD-2847" inside the order object. This matches the default output of the npm xml2js package and Python's xmltodict library — the widest downstream compatibility of any convention currently in use.
Supported Node Types: Elements, Attributes, Text, CDATA, Comments, Namespaces
The parser correctly handles element nodes, attribute maps, plain text content, CDATA sections, XML comments (optional retention), and namespace-qualified element names. DTD declarations and external entity references are stripped by design, eliminating the XXE attack surface entirely.
How to Use It
Step-by-Step Instructions
- Paste XML into the left pane, or click Try Example to load a realistic order payload automatically.
- Select conversion direction — XML→JSON is the default. Flip the toggle for JSON→XML.
- Click Convert, or enable the auto-convert toggle to update the output on every keystroke.
- Copy or Download the output using the buttons in the right pane header. Download saves as
output.jsonoroutput.xmldepending on direction.
Worked Example: Order Payload XML → JSON
The following XML represents an e-commerce order with two line items, XML attributes on both the order element and each item element, and a CDATA section for a shipping note.
Input XML
<?xml version="1.0"?>
<order id="ORD-2847" status="shipped">
<customer>
<name>Alex Chen</name>
<email>alex.chen@acme.com</email>
</customer>
<items>
<item sku="PRD-445">Wireless Keyboard</item>
<item sku="PRD-891">USB-C Hub</item>
</items>
<metadata><![CDATA[Shipped via FedEx on 2024-01-15]]></metadata>
</order>
Conversion Steps
- Paste the XML above into the left pane (or click Try Example to load it automatically).
- Confirm the direction toggle is set to XML → JSON (the default).
- Click Convert — the right pane populates with formatted JSON within milliseconds.
- Observe that
<order id="ORD-2847" status="shipped">produces top-level keys"@id"and"@status"inside the"order"object. - The two
<item>elements collapse into a JSON array because two sibling elements share the same tag name. - The CDATA section in
<metadata>appears as the plain string"Shipped via FedEx on 2024-01-15"— no CDATA wrapper syntax remains. - The
<?xml version="1.0"?>declaration is absent from the JSON output; it carries no semantic data and is dropped. - Click Copy to copy the JSON to the clipboard, or Download to save as
output.json.
Expected JSON Output
{
"order": {
"@id": "ORD-2847",
"@status": "shipped",
"customer": {
"name": "Alex Chen",
"email": "alex.chen@acme.com"
},
"items": {
"item": [
{ "@sku": "PRD-445", "#text": "Wireless Keyboard" },
{ "@sku": "PRD-891", "#text": "USB-C Hub" }
]
},
"metadata": "Shipped via FedEx on 2024-01-15"
}
}
"@id","@status"- XML attributes on the
<order>element, serialized with the@prefix. "item": [...]- Two sibling
<item>elements automatically collapsed into a JSON array. "#text": "Wireless Keyboard"- Text content alongside a
skuattribute requires the#textkey to avoid collision with the@skuattribute key. "metadata": "Shipped via FedEx on 2024-01-15"- CDATA delimiters stripped; inner content promoted to a plain JSON string.
Handling XML Attributes and Namespaces
Why Attributes Become @-Prefixed Keys
JSON objects have no native concept of "attribute vs. child element" — both map to key/value pairs. The @ prefix is a widely adopted disambiguation convention: it keeps attributes and child elements in the same JSON object without key collisions, and it signals to downstream code that a key represents metadata rather than content. In JavaScript, access the converted attribute as obj.order["@id"] — bracket notation is required because @ is not a valid identifier character in dot notation.
XML Namespace Prefixes in JSON (xmlns, ns: prefixes)
Namespace-qualified element names like <soap:Body> are preserved as-is: the colon is retained in the JSON key ("soap:Body"). Namespace declarations — xmlns="..." or xmlns:soap="..." — are serialized as "@xmlns" or "@xmlns:soap" attribute keys respectively. Namespace URIs are not resolved or expanded; you get the prefix exactly as it appeared in the source XML. SOAP envelopes and Atom feeds round-trip cleanly without losing namespace context.
Comparison with Alternative Conventions (BadgerFish, GData, Parker)
The @-prefix approach is not the only option. The BadgerFish convention uses $ as the attribute prefix and wraps text content in a $ key, making <item id="42">Hello</item> into {"item": {"$": "Hello", "@id": "42"}}. GData uses a similar $t key for text. The Parker convention intentionally drops all attributes — output is clean but lossy, making it unsuitable for any round-trip use case. This tool uses the @-prefix convention because it is the default for the npm xml2js package (10M+ weekly downloads) and xml-js, and it matches Python's xmltodict library.
| Convention | Library / Tool | Attribute Key | Text Content Key | JSON Output |
|---|---|---|---|---|
| @prefix (this tool) | xml2js, xml-js, xmltodict | @id |
#text |
{"item":{"@id":"42","#text":"Hello"}} |
| BadgerFish | Various online converters | @id |
$ |
{"item":{"$":"Hello","@id":"42"}} |
| Parker | Parker convention tools | (dropped) | direct value | {"item":"Hello"} |
| GData / Atom | Google Data APIs | @id |
$t |
{"item":{"$t":"Hello","@id":"42"}} |
Working with Repeated Elements and Arrays
How Single vs. Multiple Sibling Elements Differ in Output
This is the most common integration bug in XML→JSON conversion. When exactly one <item> element exists in a parent, the default output is a plain JSON object: "item": {"@sku": "PRD-445", "#text": "Wireless Keyboard"}. When two or more <item> elements exist at the same level, the converter wraps them in a JSON array: "item": [...]. Consumer code that accesses data.items.item[0] will throw a TypeError on the single-item case because indexing an object returns undefined. This behavior is a known footgun — the xml2js docs address it directly via the explicitArray option.
Force-Array Option for Predictable Output
Enable Force Array mode to always emit a JSON array for specified element names, regardless of sibling count. With force-array on, a single <item> produces "item": [{"@sku": "PRD-445", "#text": "Wireless Keyboard"}] — an array of one. Consumer code never needs to branch on type. This maps directly to xml2js's explicitArray: true option, which the library recommends for production use.
RSS/Atom Feed Example: <item> Always an Array
RSS feeds illustrate the problem cleanly: a channel with a single post has one <item>; a busy feed has 50. Any consumer that calls feed.channel.item.forEach() will fail silently on single-item feeds unless force-array is enabled. For a broader discussion of format trade-offs across RSS, JSON Feed, and Atom, see the XML vs JSON vs YAML vs CSV — format comparison guide.
CDATA, Comments, and Processing Instructions
CDATA Sections → Unwrapped String Value
The <![CDATA[...]]> delimiters exist in XML purely to escape characters that would otherwise need entity-encoding — angle brackets, ampersands, and so on. JSON strings handle those characters natively. The converter strips the CDATA wrapper and promotes the inner content directly: <metadata><![CDATA[Shipped via FedEx on 2024-01-15]]></metadata> becomes "metadata": "Shipped via FedEx on 2024-01-15". No #cdata key, no special escaping — just the string. Per the W3C XML specification §2.7, CDATA sections are syntactic sugar for text nodes, so treating them as plain strings is semantically correct.
XML Comments — Stripped by Default, Optional Retention
XML comments (<!-- ... -->) are metadata annotations for human readers; they carry no data semantics. The W3C XML spec defines comments in §2.5. By default this tool strips them from JSON output entirely. Enable the Preserve Comments toggle to retain them as "#comment" array entries alongside their sibling nodes. Comment preservation makes XML→JSON→XML round-trips order-sensitive — rebuilt XML places comments at the end of their parent element, not necessarily at the original position.
Processing Instructions (<?...?>) Handling
Processing instructions such as <?xml-stylesheet type="text/xsl" href="style.xsl"?> are dropped unconditionally during XML→JSON conversion — there is no toggle to retain them. If your XML→JSON→XML pipeline must preserve stylesheet references or other PIs, re-inject them manually into the XML output after conversion.
XML-to-JSON Naming Conventions Compared
The table below shows the same XML fragment serialized under four different conventions. Side-by-side output makes clear why Parker is unsuitable for round-trips and why @-prefix has been adopted by the majority of library implementations.
| XML Construct | Example XML | JSON Key | JSON Value | Notes |
|---|---|---|---|---|
| Element attribute | <order id="ORD-2847"> |
"@id" |
"ORD-2847" |
All attributes use @ prefix |
| Element text content (text only) | <name>Alex Chen</name> |
"name" |
"Alex Chen" |
No sibling elements or attributes — text is the direct value |
| CDATA section | <metadata><![CDATA[text]]></metadata> |
"metadata" |
"text" |
Delimiters stripped; inner content is a plain string |
| XML comment (default — off) | <!-- shipping note --> |
(omitted) | (omitted) | Stripped unless Preserve Comments is enabled |
| XML comment (preserve — on) | <!-- shipping note --> |
"#comment" |
"shipping note" |
Array entry alongside sibling nodes |
| Namespace prefix | <soap:Body> |
"soap:Body" |
(child object) | Colon retained; URI not resolved |
| Repeated sibling elements (2+) | <item>A</item><item>B</item> |
"item" |
["A","B"] |
Automatic array wrapping when count ≥ 2 |
| Single child element (no force-array) | <item>A</item> |
"item" |
"A" |
Object, not array; enable Force Array for consistency |
The @-prefix convention is the default for the npm xml2js package, which averages over 10 million weekly downloads — making it the most common serialization target in Node.js codebases. Python developers using xmltodict get the same @-prefix output by default. Parker intentionally discards attribute data, making round-trips impossible. BadgerFish's $-prefix is semantically equivalent to @-prefix but has far less library support.
| Library / Tool | Language | Attribute Convention | Array Behavior | Round-trip Safe? |
|---|---|---|---|---|
| xml2js | Node.js | @-prefix (default) |
Object if 1, array if 2+ (explicitArray: false default) | Yes (elements + attributes) |
| xmltodict | Python | @-prefix (default) |
Object if 1, array if 2+ (force_list option available) | Yes (elements + attributes) |
| fast-xml-parser | Node.js | Configurable (default: no prefix) | Configurable via isArray callback | Partial (depends on config) |
| This tool | Browser | @-prefix |
Auto-array + optional Force Array | Yes (comments/PIs lossy) |
| BadgerFish tools | Various | @-prefix (same as xml2js) |
Explicit array syntax | Yes (using $ for text) |
Behind the Scenes: Conversion Algorithm
XML Parsing: DOMParser Web API
The tool uses the browser-native DOMParser API — no third-party XML parser is shipped to the client. new DOMParser().parseFromString(xmlString, "application/xml") returns a DOM Document object conforming to the WHATWG DOM specification. If the XML is malformed, DOMParser returns a document containing a <parsererror> element, which the tool detects and surfaces as a human-readable error message before any JSON output is generated.
Tree Traversal and Node-Type Dispatch
The converter walks the DOM tree recursively. At each node it branches on the W3C DOM node type constant: ELEMENT_NODE (1) triggers attribute collection and child recursion; TEXT_NODE (3) captures text content; CDATA_SECTION_NODE (4) extracts the inner data string exactly as a text node would; COMMENT_NODE (8) is either dropped or emitted as a #comment entry depending on the Preserve Comments setting. Attributes are collected by iterating element.attributes, a live NamedNodeMap — each attribute's name becomes "@" + name and value becomes the string value.
JSON→XML Reconstruction: XMLSerializer and createElement
Reverse conversion reads the JSON object recursively. Keys starting with @ are applied via element.setAttribute(key.slice(1), value). The #text key produces a text node via document.createTextNode(value). All other keys produce child elements via document.createElement(key). Once the DOM tree is built, new XMLSerializer().serializeToString(doc) renders it back to a string, which is then pretty-printed with a simple indentation pass.
RFC 7158 / ECMAScript JSON Compliance
JSON output is serialized via JSON.stringify(obj, null, 2), producing a two-space-indented, spec-compliant JSON string. The output conforms to RFC 7158 (which supersedes RFC 4627) — the standard governing JSON interchange format validation. All string values are properly escaped; no non-standard extensions (comments, trailing commas) are introduced.
Performance and Limitations
Practical Input Size Limits
Browser DOMParser handles up to approximately 10 MB of XML without noticeable lag on Chrome 120 and Firefox 122 on a mid-range development machine. Beyond that, the synchronous DOM parse blocks the main thread; a 50 MB XML file will parse but may freeze the tab for several seconds. For files over 10 MB, run the conversion server-side with xml2js or a streaming SAX parser instead.
DTD and External Entity References
DTD declarations (<!DOCTYPE ...>) and external entity references are stripped entirely. Browser DOMParser does not fetch external DTDs, eliminating the XML External Entity (XXE) attack surface by design. If your XML relies on entity definitions in a DTD, pre-expand those entities before pasting into this tool.
Number and Boolean Type Coercion
XML has no native type system — all values are strings. By default, <qty>3</qty> outputs as the JSON string "3", not the number 3. Enable the Type Coercion toggle to have numeric strings coerced to JSON numbers and "true"/"false" strings coerced to JSON booleans. Use coercion carefully: a value like "007" will become the number 7, losing the leading zero.
Schema-Less Conversion Caveats
The tool does not validate XML against an XSD schema before converting. Structurally valid XML that violates a schema constraint will convert without error. Use the XML Validator to check schema conformance before conversion if that matters for your pipeline. Deeply nested XML (beyond ~50 levels) converts without error but produces JSON objects that are unwieldy to navigate manually.
| XML Node Type | Example XML Fragment | JSON Key | JSON Value Example |
|---|---|---|---|
| Element with attributes | <order id="ORD-2847" status="shipped"> |
"@id", "@status" |
"ORD-2847", "shipped" |
| Element with text content only | <name>Alex Chen</name> |
"name" |
"Alex Chen" |
| CDATA section | <![CDATA[Shipped via FedEx]]> |
(parent element key) | "Shipped via FedEx" |
| XML comment | <!-- fulfillment note --> |
"#comment" (if preserved) |
" fulfillment note " |
| Namespace-qualified element | <soap:Body xmlns:soap="..."> |
"soap:Body", "@xmlns:soap" |
(child object), "http://..." |