Converting between JSON and XML: how it works and what breaks

Guide · Updated July 2026 · ~7 min read

Converting JSON to XML or back looks like it should be mechanical. It mostly is — but the two formats model data differently, so every converter has to make judgment calls, and some information genuinely cannot survive the trip. Knowing the rules saves you from silent data corruption.

Try the examples as you read: JSON → XML andXML → JSON run side by side in your browser.

The fundamental mismatch

JSON is a tree of typed values: objects, arrays, strings, numbers, booleans, null. XML is a tree of elements that can carry attributes, text, comments, and child elements — all of it untyped text. Converting between them means bridging four gaps:

JSON → XML: the easy direction

Object keys become element names and values become content:

{
  "user": {
    "name": "Ana",
    "age": 31,
    "tags": ["admin", "editor"]
  }
}
<user>
  <name>Ana</name>
  <age>31</age>
  <tags>admin</tags>
  <tags>editor</tags>
</user>

Even in this direction there are decisions to make:

XML → JSON: where it gets opinionated

The reverse direction is where converters disagree, because XML carries more structure than JSON can express.

Attributes need a convention

<user id="17" role="admin">Ana</user>

JSON has nowhere native to put id and role, so converters adopt a marker convention — commonly an @ prefix for attributes and a special key for text content:

{ "user": { "@id": "17", "@role": "admin", "#text": "Ana" } }

Different tools use different markers (@id vs _id vs anattributes object), which is why XML→JSON output from one library often won’t round-trip through another.

One element or many? The array ambiguity

This is the classic conversion bug. Repeated elements clearly form an array:

<roles><role>admin</role><role>editor</role></roles>
{ "roles": { "role": ["admin", "editor"] } }

But when the same document happens to contain only one <role>, a naive converter produces a plain value instead of a one-item array — and the consuming code that expected an array crashes, but only on single-item data. If you control the consumer, handle both shapes; if you control the conversion, force known-repeating elements to always be arrays.

Types are guesses

XML text is untyped, so a converter seeing <age>31</age> must decide: the number 31 or the string "31"? Auto-detection usually helps, but it famously mangles values like <zip>01234</zip>(leading zero lost as a number) or version strings like 1.10. Check how your converter treats numeric-looking strings, booleans (true vs"true"), and empty elements (null, "", or{}?).

Some XML simply doesn’t fit

Comments, processing instructions, CDATA boundaries, namespaces, and mixed content(text interleaved with child elements, as in any HTML-like paragraph) have no faithful JSON representation. Converters drop or approximate them. If your XML is document-like rather than record-like, converting it to JSON is usually the wrong move — seeJSON vs XML for when each format fits.

What survives a round trip

A round trip — XML → JSON → XML, or the reverse — is the clearest way to see what a converter throws away, and it is worth running once against a representative document before you build a pipeline on top of one. Start here:

<!-- original -->
<article lang="en">
  <!-- editorial note -->
  <title>Ada</title>
  <body>See <em>this</em> page</body>
</article>
<!-- after XML → JSON → XML -->
<article lang="en">
  <title>Ada</title>
  <body>
    <em>this</em>
  </body>
</article>

The comment is gone, and — more seriously — the mixed content in <body>has lost the words around the <em> and the order they appeared in. The document still parses, still validates as well-formed, and now means something different. This is the failure mode to fear: not a crash, but a quiet change nobody notices until a reader complains.

Record-shaped XML — elements containing either text or child elements, never both — round trips cleanly apart from comments and type spelling. Document-shaped XML does not. That distinction, rather than any property of a particular library, is what determines whether conversion is safe.

Doing this in code

For anything beyond a one-off, use a library with explicit options rather than whatever the first search result suggests — the defaults are where the surprises live:

Whichever you pick, pin the attribute-marker convention explicitly in your configuration. Leaving it at the default is how a library upgrade silently renames every key in your output.

Practical rules of thumb

Try it

Paste a document into the JSON → XML converter or theXML → JSON converter and compare input and output side by side. Everything runs locally in your browser — nothing is uploaded.

Keep reading