JSON to XML or YAML? What Each Conversion Loses

2026-11-057 min readJSON Tools5 tools covered

Round-trip a document through XML or YAML and it rarely comes back identical. Numbers turn into strings, comments disappear, lists get flattened. Choose a format by what it loses, not by which syntax looks tidier - that is what this page covers.

Key takeaways
  • XML carries no types, so every text node converts back to a JSON string and <age>20</age> becomes "20"
  • The YAML to JSON direction is a subset parser: block scalars, anchors and flow syntax degrade silently into literal strings instead of raising an error
  • A JSON key is not automatically a legal XML tag name; keys with spaces, a leading digit or & produce invalid XML, and the tool will not silently rename them
  • Each format expresses lists differently - repeated sibling tags in XML, dashes in YAML, brackets in JSON - and that is the only structure that survives a round trip
  • Minifying JSON re-serialises the parsed value, so 1.0 becomes 1, 1e3 becomes 1000, and integers above 2^53 lose precision

#What each format throws away

Conversions lose information because the three formats do not have the same expressive power - not because a tool is badly written. The losses cluster in three places: types, comments, and how lists are represented.

JSON has six value types. XML has elements, text and attributes. YAML looks the most flexible, but the subset that can be parsed safely is surprisingly narrow. Going out is always easier than coming back.

From → ToWhat is lostRecoverable?
JSON → XMLNumber and boolean types become text; null loses its meaningRestore per field on the way back
XML → JSONAttributes become @key; stray text in mixed content is droppedOnly child elements survive
JSON → YAMLRarely anything, though empty-value style may changeQuote values to keep "true" distinct from true
YAML → JSONBlock scalars, anchors, merge keys, tagsDegrades silently - check the syntax first

#JSON to XML: every value becomes text

On the way back from XML, every text node is a string. The tool never guesses types. <age>20</age> becomes "20" and <ok>true</ok> becomes "true".

That is deliberate. XML does not carry type information, and guessing always breaks something: 007 becomes 7 (leading zeros matter for employee and flight numbers), 1.0 becomes 1, and 1.10 could be either a version or a number.

Note

Returning strings everywhere puts the decision in the consumer's schema, where it belongs. A converter that guesses types is making a call it cannot justify.

Using JSON to XML, two rules are fixed:

  • The root tag is always <root>, and object keys become tag names directly.
  • Each array element becomes a repeated sibling tag, not an indexed <item index="0"> container. The latter has no common convention in XML; repeated tags do.

The reverse direction has three fixed mappings too: attributes become keys prefixed with @ (<book id="1"> → "@id": "1"), repeated sibling tags merge into an array, and an element with only attributes gets its text under #text. A round trip through these rules is lossless.

Warning

A JSON key is not automatically a legal XML tag. Keys containing spaces, starting with a digit, or holding & or < produce invalid XML. The tool will not rename them, because renaming quietly changes your data model. Normalise the keys to underscores or camelCase first, or switch to YAML, which places no such restriction on key names.

#JSON to YAML: most readable, but the parser is a subset

The YAML to JSON direction uses a self-contained subset parser, not a full YAML implementation. It handles block mappings, block sequences, indentation-based nesting, whole-line comments, quoted strings, and the --- / ... document markers.

Unsupported syntax degrades silently, which is the part that bites:

YAML
# Typical of Kubernetes manifests and GitHub Actions workflows
command: |
  npm install
  npm test

Parse that with JSON to YAML in the YAML to JSON direction and you get "command": "|". The indented block underneath is skipped entirely. No error, just a truncated result - worse than a failure, because nothing warns you.

Anchors and references (& *), merge keys (<<), flow style ({a: 1}, [1, 2]) and tags (!!str) are unsupported too. Before converting a real CI config, check that the file does not use any of them.

Two type details: yes / no / on / off are booleans in YAML 1.1 but are treated as strings here, and a trailing comment (key: value # note) is not stripped - the # and everything after it end up inside the value. Put comments on their own line.

Tip

If a value comes back from YAML with a stray # comment appended, that is not a bug. It is a trailing comment being read as part of the value. Move it to its own line and convert again.

#XML beautify and minify: comments depend on direction

With the XML formatter, whether comments survive depends on which direction you pick. Minify drops them wholesale - the usual way to save bytes - while it keeps CDATA, DOCTYPE and processing instructions. Beautify keeps comments exactly as written.

Beautify indents with two spaces and keeps <title>x</title> on one line when an element holds a short piece of text, rather than splitting it across three lines.

Tokenising is strict about order:

  • CDATA, then comments, then processing instructions, then DOCTYPE, then ordinary tags
  • Quotes inside a tag stay protected, so a > in an attribute value never truncates the tag

That is exactly where regex search-and-replace fails: a single s/></>\n</g rewrites the > inside attributes too.

Warning

Pure data XML is fine, but mixed content gets rewritten: the space in <p>Hello <b>world</b></p> may be swallowed. Do not reflow layout-sensitive XHTML with this. The tool only lays out markup and does not check whether tags are closed; to validate a document, parse it with JSON to XML in the XML to JSON direction, which uses the browser's native parser and reports syntax errors directly.

#Look at the structure before you ship it

After converting, confirming the hierarchy visually is faster than reading the output text. Open it in the JSON viewer, which expands to depth 3 by default, offers expand-all and collapse-all, and reports the node count and maximum nesting depth in the status bar.

It is read-only: no editing, no export. To change values, use the JSON formatter and validator and come back. Nodes are built with textContent, so a JSON string containing <script> shows as text and never runs.

If the goal is a smaller payload, use JSON minify and escape. Its "minify" is JSON.parse followed by re-serialisation, not whitespace stripping, and that has consequences:

  • 1.0 becomes 1, and 1e3 becomes 1000
  • Duplicate keys keep only the last value
  • Integers above 2^53 (9007199254740992) are rounded to the nearest double

An 18 or 19 digit snowflake ID or order number will come out as a different number. Make sure such fields are strings before you minify.

#Which tool to use

What you needToolWatch out for
Feed JSON to a legacy Java or SOAP systemJSON to XMLKeys must be legal tag names
Write config for Kubernetes or CIJSON to YAMLThe reverse direction cannot read `\` blocks
Clean up XML someone sent youXML formatterMinify drops comments
Understand a large JSON payloadJSON viewerSlow past roughly 10k nodes
Embed JSON in a log line or a code stringJSON minify and escapeRe-serialises; large integers lose precision
Just validate and prettify JSONJSON formatter and validatorErrors come with line and column

Work backwards when choosing: whatever the downstream system can read wins. Service-to-service data is fine as JSON. Config that humans and tooling read is better as YAML. Anything talking to a legacy system, a SOAP endpoint or an Office document has to be XML.

#Frequently asked questions

#Why did my numbers become strings after converting to XML?

XML has no separate number type; element content is text. The tool does not guess on the way back, so 20 becomes "20". If the consumer needs numbers, cast them against a schema there. Guessing in the converter would destroy values like 007 that depend on leading zeros.

#What are | and > in YAML, and why can they not be converted?

They mark block scalars. | keeps line breaks, > folds them into a paragraph. Both require a real YAML parser, and the reverse direction of JSON to YAML is a subset implementation - it reads | as a literal string and skips the indented block that follows.

#Why did my ID change after minifying?

Because minifying re-serialises the parsed value, and JSON numbers are IEEE-754 doubles. Anything above 2^53 cannot be represented exactly, so 1234567890123456789 becomes 1234567890123456800. Ship snowflake IDs and order numbers as strings rather than avoiding the minifier.

#Can I get comments back after minifying XML?

No. Comments are dropped wholesale and leave no trace in the output. Use the beautify direction if you need them, or keep the original file.

#Which conversion is safest?

JSON to YAML and back is the smoothest, because both share the object / array / scalar model - the differences are syntactic. Once XML is involved you must resolve "element or attribute" and "text has no type", so a round trip always loses something. Avoid round trips when you can.

Tools used in this guide

Related guides