100% local — your data never leaves your browser

XML to CSV — One Row per Element

Turn repeated XML elements into rows. The child names become the columns, ready to open in a spreadsheet or feed to a script.

Instant Private Zero cookies
Delimiter

XML input

CSV output

What this tool does

It finds the repeated element and makes each occurrence a row.

<orders>
  <order id="1"><customer>Ada</customer><total>42</total></order>
  <order id="2"><customer>Alan</customer><total>17</total></order>
</orders>
customer,total,id
Ada,42,1
Alan,17,2

Single-key wrappers are walked through, so <orders><order>… needs no configuration. A wrapper carrying its own attribute — <orders count="2"> — is walked through too: that attribute describes the collection, not a row, so it does not become a column. An attribute on a row does.

Attributes are columns

An identifier that lives in an attribute is data like any other. @_id becomes the column id.

The prefix is kept in exactly one case: when a child element already carries that name. <row id="1"><id>x</id></row> gives two columns, id and @_id, because they hold two different things and merging them would make one of them disappear. A toggle drops attributes entirely if your table should come from the elements alone.

One row is a table

An export with a single record converts like any other. That is worth saying because it did not use to: XML-to-JSON turns a non-repeated element into an object rather than an array, and requiring the array rejected a perfectly ordinary document.

A root element containing nothing but text is still refused — one value is not a table, and pretending otherwise would help nobody.

Rows that do not match

The header is the union of everything seen, in first-seen order. A row missing a column gets an empty cell:

<r><row><a>1</a></row><row><a>2</a><b>3</b></row></r>
a,b
1,
2,3

An irregular export is normal. Dropping the extra column, or the row that lacks it, would lose data to tidiness.

What a cell cannot hold

A cell holds one value. When an element contains a subtree, that subtree is written as JSON inside the cell rather than spread across invented column names. It is ugly and it is honest: nothing is lost, and you can see exactly what was there.

Private by design

Everything runs locally in your browser with JavaScript. Your data is never uploaded, which makes the tool safe for sensitive content, and it keeps working offline.

Frequently asked questions

Where did my id attribute go? It used to disappear.
It is a column now. `<row id="1">` gives an `id` column beside the ones that come from child elements — an identifier living in an attribute is still data, and dropping it silently was a real defect in this tool, found while writing this page. If a child element is already called `id`, the attribute keeps its `@_` prefix so the two columns stay distinct.
Why does a nested element show up as JSON in the cell?
Because a CSV cell holds one value and your element holds a subtree. Flattening it into `parent.child` columns would invent a naming convention your consumer has not agreed to; JSON in the cell is at least unambiguous and reversible. Flatten before converting if you want real columns.
My rows do not all have the same children. What happens?
The header is the union of every element and attribute seen, in first-seen order, and a row missing one gets an empty cell. Nothing is dropped for being irregular — an export where only some records carry a middle name is normal, not an error.

Related converters