XML to YAML: How Elements, Attributes, and Text Actually Map Over
A practical guide to converting XML to YAML: how elements become keys, attributes get a prefix, repeated tags turn into lists, and where the ambiguities hide.
XML to YAML: How Elements, Attributes, and Text Actually Map Over
I spent an afternoon last month trying to read a 400-line app.config someone had written in 2014. It was XML. Every line had an opening tag, a closing tag, and a small forest of angle brackets in between, and by the time my eyes reached </configuration> I had lost track of which <add> belonged to which <section>. I pasted the whole thing into an XML to YAML converter, and the same structure suddenly fit on a screen I could actually scan. That is the real reason this conversion is worth doing: not because YAML is fashionable, but because for configuration it is genuinely easier to read.
This post walks through how an XML tree maps onto YAML, what the converter decides for you, and the handful of ambiguities you should know about before you trust the output.
Why YAML Reads Better Than XML for Config
XML was designed for documents and machine exchange. It is explicit to a fault: every element is bracketed twice, once to open and once to close, so a deeply nested file spends a lot of ink repeating tag names. That redundancy is great for a parser and tiring for a human.
YAML drops the closing tags and the angle brackets entirely. Nesting is expressed by indentation, the way you would draw an outline on paper. A three-level structure that costs six tags in XML costs three indented lines in YAML. When you diff two versions of a config in git, the YAML diff shows you the line that changed; the XML diff often shows you a closing tag moving around. For files a person edits by hand, that difference compounds fast.
None of this makes YAML "better" in the abstract. For a SOAP envelope or a signed document, XML's strictness is the point. But the moment a format becomes a config file that humans open and edit, readability wins, and that is exactly the migration path most teams are on.
The Core Mapping: Elements Become Keys
The base rule is simple. An XML element becomes a YAML key, and whatever is inside it becomes that key's value.
A leaf element with only text becomes a key with a scalar value. <title>SICP</title> becomes title: SICP. An element with child elements becomes a key whose value is a nested mapping, and each child repeats the rule one level down. The indentation that YAML uses to show that nesting is the whole readability payoff: the tree shape you had to reconstruct mentally from the XML is now drawn out for you.
Two derived rules carry most of the weight in real documents, and they are where the conversion stops being obvious.
Attributes Get a Prefix
XML has two ways to attach data to an element: child elements and attributes. YAML mappings only have keys. So an attribute has to become a key, but it cannot just borrow the attribute's name, because an element could have an attribute and a child element with the same name and they would collide.
The fix is a prefix. By default the converter prepends @ to attribute names, so they sit in the same mapping as the child keys but never clash. Switch the prefix to $ if your downstream parser follows the xml2js convention. The prefixed keys come out quoted in the YAML, because @ is a reserved indicator character in YAML and a bare key cannot start with it.
So <book id="b1" lang="en"/> becomes:
book:
"@id": b1
"@lang": en
Once you have seen the @ convention a couple of times it reads naturally: anything with the prefix was an attribute in the source, everything else was an element.
Repeated Elements Become a List
In XML, you express "many of these" by simply writing the element again. Five <item> siblings are five separate tags. YAML has a real sequence type, so the converter collapses same-name siblings into a list.
This is the rule that catches people, so here is the precise behavior: an element name that appears more than once among its siblings becomes a YAML list. A name that appears exactly once stays a single scalar or mapping, not a one-item list. That keeps the output looking like hand-written YAML, but it means the shape of the output depends on the data. Two <item> elements give you a list; one gives you a plain value. If your code always expects a list, feed it at least two items while testing, or normalize the shape so it accepts both.
A Worked Example
Here is a small document that exercises all three rules at once. The input XML:
<library>
<book id="b1" lang="en">
<title>SICP</title>
<tag>cs</tag>
<tag>lisp</tag>
</book>
</library>
And the YAML it converts to:
library:
book:
"@id": b1
"@lang": en
title: SICP
tag:
- cs
- lisp
Read it top to bottom. library and book are elements, so they became nested keys. id and lang were attributes, so they carry the @ prefix and stayed quoted. title appeared once, so it is a plain scalar. tag appeared twice, so it collapsed into a list. The closing tags and the angle brackets are gone, and the indentation does the work they used to do. Nine lines of XML became eight lines of YAML you can actually scan, and the structure survived intact. Paste your own document into the XML to YAML converter and watch the same three rules fire as you type.
The Ambiguities Worth Knowing
Three things are not as clean as the rules above suggest, and pretending otherwise will bite you later.
First, mixed content. An element can hold both text and child elements at the same time, like <p class="lead">hello</p> where hello is loose text sitting next to a class attribute. YAML mappings have no slot for "the text of this node," so the loose text is parked under a #text key by default. You can rename that key, or drop the stray text entirely if you only want the structured children.
Second, type coercion. A value like 007 or yes or true looks like a number or a boolean, and a naive YAML serializer would emit it bare, at which point 007 reads back as the integer 7 and yes reads back as a boolean. The converter quotes ambiguous scalars on purpose to keep them strings. The catch is that this protection lives in the quotes. If you hand-edit the YAML afterward and strip those quotes, the coercion comes back. Leave the quotes the tool added in place.
Third, the round-trip is lossy. Once you reshape XML into YAML, the distinction between an attribute and a single-child element blurs, comments are dropped, and element order across different names is no longer load-bearing. You cannot reliably convert the YAML back into byte-identical XML. If you need a reversible path, run the document through the XML to JSON converter instead and treat the JSON as your source of truth, since the object model there maps back more predictably.
When to Reach for a Different Tool
XML to YAML is the right move when the goal is a readable config file: a Helm values file, a Kubernetes manifest, an Ansible vars block, a GitHub Actions workflow. If you are pulling tabular data out of XML rather than configuration, a flat sheet beats a nested tree. And if you already have YAML that is just untidy, you do not need a converter at all.
For those cases, keep the conversion honest by reaching for the tool that fits the output you actually want, and convert once rather than fighting the wrong format. The mapping rules in this post stay the same; only the destination changes.
Made by Toolora · Updated 2026-06-13