YAML to XML / CSV Converter
Convert YAML to XML, XML back to YAML, or list-shaped YAML to CSV/TSV — every dropped construct counted — entirely on this device.
CSV and TSV need a top-level sequence of mappings — one mapping per row. XML converts any document. Wraps the document when the top level is not a single-key mapping. Element name given to each entry of a sequence. Keys written "@id" become id="…" attributes instead of child elements. Collection keys have no element-name form — write them as quoted flow-scalar text instead of refusing. How a --- multi-document stream converts: one sequence, only the first document, or a refusal. YAML timestamps become ISO 8601 text, or stay exactly as written. .nan and .inf have no XML text form — refuse, or write an empty value with a warning.
Press ? for shortcuts
Do it offline:$ yq -o=xml '.' config.yaml
How to use it
- Type, paste or drop a file up to 20 MB — the badge names the direction it read, and a click pins the other one.
- Choose the output target on the YAML side: XML, or CSV/TSV when the top level is a sequence of mappings.
- Name the root and item elements, and set how attributes come back on the XML side.
- Read the report, then copy, download .xml/.yaml/.csv/.tsv, or pack the setup into a Share link.
Paste a manifest to get XML, paste XML to get a manifest back, or switch the output target to CSV or TSV when the document is a list of rows. The two formats hold different things, so nothing is dropped quietly: expanded aliases, removed comments, renamed keys and vanished empty lists are each counted in a report under the result. Up to 20 MB, on this device.
How it works
The gap is structural, not cosmetic. An XML element carries attributes and can hold text and children woven together; a YAML mapping has neither. A YAML anchor is a pointer reused across a document and a tag names a type; XML spells neither. Whatever the target has no slot for is resolved into something it does have, and the resolution is recorded.
Element names are the second gap. XML restricts a name to the Name production — no spaces, no leading digit — while a mapping key is any scalar. An illegal name becomes a generic element carrying the original key in an attribute, so the text survives where the name cannot.
Examples
# defaults
web:
image: &img nginx:1.25
ports:
- 80
- 443
alias: *img
<?xml version="1.0" encoding="UTF-8"?>
<web>
<image>nginx:1.25</image>
<ports>80</ports>
<ports>443</ports>
<alias>nginx:1.25</alias>
</web>A sequence becomes repeated sibling elements — two <ports>, not a list wrapper. The alias arrives as a copy of its anchor, reported as 1 anchor expanded; the header comment is counted as dropped.
my key: 1
2fa: on
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item name="my key">1</item>
<item name="2fa">on</item>
</root>Neither key is a legal element name — one holds a space, the other leads with a digit — so both become <item> carrying the key in a name attribute, one warning each.
<root><a id="1">hi</a><!-- c --><a>2</a></root>root:
a:
- "@id": "1"
"#text": hi
- "2"
Coming back, the attribute becomes an @-prefixed key and the element text a #text key. Two <a> siblings form a sequence; the comment between them is gone.
- id: 1
name: alpha
- id: 2
name: beta
id,name
1,alpha
2,beta
Headers come from the union of the keys, rows keep the document's order, lines end CRLF per RFC 4180. TSV is the same table, tab-separated.
when: 2001-12-14
nul: ~
empty:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<when>2001-12-14T00:00:00.000Z</when>
<nul/>
<empty/>
</root>The edge case: a bare date is a YAML timestamp and becomes ISO 8601 text, counted. An explicit ~ and a valueless key end as the same empty element — indistinguishable coming back.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Anchors and aliases arrive as copies
Each alias is written out in full wherever it appears, counted. Sharing is not a fact XML records, so editing one copy no longer edits the others.
Comments do not survive either direction
A # line going out and a <!-- --> coming back are both removed and counted. Keep the annotated file as your source.
A bare date leaves as ISO 8601 text
2001-12-14 matches the YAML timestamp form and is written 2001-12-14T00:00:00.000Z, counted. Setting dates to original passes it through as typed.
Empty is one shape out, two shapes lost
A null, an explicit ~ and a bare key all become the same empty element. An empty sequence has no form at all: no element is emitted, and a warning names the key.
Keys outside the XML Name production get renamed
A space, a leading digit or a slash disqualifies a name. That value goes into a generic element carrying the key in a name attribute, warned once per key.
Attributes and mixed content come back flattened
Reading XML, attributes become @-prefixed keys and text beside child elements a #text key. Neither records how text and elements interleaved, so inline-marked-up prose converts but does not reassemble.
Common errors
Tabs are not allowed as indentation at line 2, column 1 — probable cause: YAML forbids tabs as indentation; replace the tab with spaces.- The YAML reader stops before anything converts, and its message reaches this page untouched — position, probable cause and caret frame. Re-indent that line with spaces.
Expected closing tag 'b' (opened in line 1, col 4) instead of closing tag 'a' at line 1, column 7 — probable cause: <b> is still open; close it before closing its parent.- An unclosed element is reported by the tag that opened it and the tag that tried to close over it. Elements close innermost first — fix the one named first.
This YAML is not a list of rows — CSV output needs a top-level sequence of mappings, one mapping per row (for example "- id: 1"). Switch the output target to XML to convert this document as it is.- A table needs rows. A mapping at the top level describes one thing, not many, so nothing goes under the headers — wrap it in a sequence, or convert as XML.
Top-level value is a single object, not an array of rows — wrap it in an array: [ { … } ].- The second line comes from the shared table writer, printed exactly as that component wrote it — same fact, its own vocabulary.
FAQ
How does a YAML sequence become XML?
As repeated sibling elements under the parent key: two ports give two <ports> elements, not a wrapper. A sequence at the top level has no parent to repeat under, so it is wrapped in the root element and each entry takes the item name you set.
Can I convert XML back to YAML here?
Yes — that direction is the same page, not a second one. The badge reads a leading angle bracket, and a click overrides the guess. Reading options replace writing options, and the download extension follows.
When can I get CSV or TSV out of YAML?
When the top level is a sequence of mappings — one mapping per row. Keys form the header union across rows, nested objects flatten to a bounded depth, and any other shape is refused with its reason.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Run the conversion; above 2 MB it waits for this.
- ⌘/Ctrl + S
- Download the result — extension per target.
- ⌘/Ctrl + C
- Copy the whole result when nothing is selected.
- ⌘/Ctrl + Shift + C
- Copy the result over a selection.
- s
- Flip direction; one undo returns.
- t
- Next theme.
- ? or ⌘/Ctrl + /
- Show the shortcuts.
- Esc
- Close the top overlay; both panes stay.