XML to CSV Converter
Paste XML or open a file and get CSV back, with your attributes kept as columns. Most converters drop them silently, so an order ID written as an attribute simply disappears — this one keeps it, prefixed with @ so it cannot clash with an element of the same name. You also choose which repeating element becomes a row rather than accepting a guess, and decide how repeated children are handled: numbered columns, one row each, or joined into a single cell. Nothing is uploaded and there is no size gate.
XML is a tree and CSV is a grid, so something has to be the row. Paste your XML and the options appear here with the row count each would give.
What it found
Paste XML above. The CSV appears below as a table so you can check the columns before you take it.
Preview
Everything runs in your browser. Your XML is never uploaded, and there is no paid tier that unlocks bigger files — the only limit is a 5MB guard that stops the tab freezing.
Runs in your browser. Nothing uploaded.
How to use it
- Paste your XML, or open a file. Read locally. Nothing is uploaded and there is no paid tier for bigger files.
- Check which element became a row. The tool picks the one that would hold the most data and says which. Every alternative is listed with the number of rows it would produce.
- Choose how repeated children are handled. Numbered columns, one row each, or joined into a cell.
- Read the report, then take the CSV. It tells you what came from attributes and which fields some records are missing.
What counts as a row when your data is a tree?
This is the whole problem, and it is why two XML-to-CSV converters can give you completely different files from the same input.
CSV is a grid. XML is a tree. Nothing in the XML says which level of that tree is “a row” — you have to decide. Take a purchase order export:
<orders>
<order id="1001">
<customer>Alice</customer>
<items>
<item sku="PEN-1">Fountain pen</item>
<item sku="INK-4">Ink, blue</item>
</items>
</order>
</orders>
Is that one row or two?
If you want one row per order, it is one — and the two items have to go somewhere. If you want one row per line item, it is two, with the customer name repeated. Both are correct. Which you need depends entirely on the question you are about to ask the spreadsheet, and no converter can know that.
So the tool shows you every repeating element it found with the row count each would give, picks one as a starting point, and lets you change it. Most converters make this choice silently and never tell you they made it.
The starting point is chosen by how much data the table would hold — rows multiplied by columns — rather than by which element simply occurs most. That distinction matters: on the file above, item occurs more often than order, so a frequency rule would hand you three SKUs with no way to tell which order any of them came from. Two orders of five columns beats three items of two, so the order wins. On a real catalogue, where a couple of sections hold hundreds of products, the products win instead — which is also right.
Why your attributes go missing elsewhere
In the example above, the order ID is an attribute: id="1001". So is the SKU.
A tree-to-grid flattener has to decide where attributes go, and the simplest implementation is to skip them. That is not a hypothetical — one of the popular browser-based converters states in its own documentation that XML attributes are not included in its output.
The result is a CSV of customer names and product descriptions with no identifiers at all. It looks plausible. It is unusable for anything you would actually do with it, and nothing warns you.
This tool keeps every attribute as its own column, prefixed with @:
| @id | customer | items_item_1 | items_item_1_@sku |
|---|---|---|---|
| 1001 | Alice | Fountain pen | PEN-1 |
The @ is not decoration. It stops an attribute called name colliding with a child element also called name — which happens constantly in real exports, and silently overwrites one of them in tools that flatten both to the same column.
Repeated children: three answers, none of them wrong
When one record contains several of the same child — line items, tags, phone numbers — there is no single correct flattening. So you pick.
| Strategy | Result | Use it when |
|---|---|---|
| Numbered columns | item_1, item_2, item_3 on one row |
The number of children is small and fairly consistent |
| One row each | Three rows, parent fields repeated on each | You are going to pivot, sum or filter by the child |
| Joined | One cell: pen; book; lamp |
You want it readable, not processable |
“One row each” is what a database person means by denormalising, and it is usually right if the children are the thing you care about. “Numbered columns” keeps one row per record, which is right if the parent is the thing you care about and the children are detail.
If a record has two different repeating groups, only the larger is expanded into rows and the other is joined. A full cross product of both would multiply your row count for no meaningful gain.
Common problems
| What you see | What it means | What to do |
|---|---|---|
| Only one row came out | Nothing in the file repeats at the level chosen | Pick a deeper element from the row selector |
| Far more rows than expected | The record path is one level too deep | Pick the parent element instead |
| Identifiers are missing | They were attributes, and attribute columns are off | Turn them back on |
| Some cells are empty | Those records genuinely lack that field | Nothing — the report names which and how many |
Columns named a_b_c |
Nested elements flattened to a path | Expected. The underscores are the tree depth |
| Excel mangles the accents | Excel guessed the encoding | Turn on the UTF-8 BOM |
Namespaces, CDATA and entities
Namespace prefixes are kept as part of the column name — ns:price stays ns:price, which is what you want in a spreadsheet where the prefix may be the only thing distinguishing two fields.
CDATA sections are read literally, so markup inside them survives as text rather than being parsed as elements.
The five entities XML predefines — & < > " ' — are resolved, along with numeric references like A. Anything else is left exactly as written. An entity such as is only defined if the document declares it, and a converter that quietly guessed would be inventing data. If you see one in your output, that is the file telling you something.
After you have the CSV
Flattened XML often carries whitespace and typographic characters from the source system that survive into the CSV and break an exact-match lookup later. If a value looks right but will not match, our Invisible Character Detector will name what is actually in the string.
If you are converting text or delimited data rather than XML, the Text to CSV Converter handles that, including columns that are simply lined up with spaces. And if the CSV is destined for a WooCommerce import, the WooCommerce Product CSV Checker validates the columns before you upload it.
What it will not do
It converts XML to CSV and reports what it did. It is not an XSLT processor, an XPath console or a schema validator, and it does not convert CSV back to XML — going the other way means inventing a structure the CSV does not contain, and the result is almost never the shape anyone wanted.
It is also not a general XML processor. It does not resolve DTDs or fetch external entities, which is deliberate: external entity resolution is a well-known security hole, and a converter has no business making network requests to read your file.
Frequently asked questions
How do I convert XML to CSV?
Paste the XML in or open a file. The tool works out which element repeats and turns each occurrence into a row, with child elements and attributes as columns. Check the record path it chose, adjust it if the rows are not what you expected, then copy or download the CSV.
Why do other XML to CSV converters lose my attributes?
Because flattening a tree into a grid needs a decision about where attributes go, and the simplest implementation is to ignore them. At least one popular browser-based converter states plainly in its own documentation that XML attributes are not included in its output. That matters because identifiers are very often attributes — in <order id="1001"> the order ID is the one thing you cannot afford to lose. This tool keeps every attribute as its own column, prefixed with @.
What is a record path and why does it matter?
XML is a tree and CSV is a grid, so something has to decide which element counts as a row. In a purchase order file, /orders/order might occur 40 times and /orders/order/items/item 300 times. Both are valid answers depending on whether you want one row per order or one row per line item. The tool starts with whichever would hold the most data, counting rows times columns rather than just frequency — picking the most frequent element alone would choose the line item every time and throw away which order it belonged to. It tells you what it chose and lists the alternatives with their row counts.
What happens when one record contains several of the same child?
You choose. Numbered columns give item_1, item_2, item_3 on a single row, which suits a fixed small number of children. One row each repeats the parent fields across several rows, which is what you want for line items you intend to pivot or sum. Joined puts all the values in one cell separated by a semicolon, which is compact and readable but harder to process further.
Are there file size limits?
Only a 5MB guard that stops a huge file freezing the browser tab, and it is a technical limit rather than a paid tier. Some converters cap the free version at 1MB and sell you more, which is worth knowing before you paste a real export into one.
Is my XML uploaded anywhere?
No. Parsing, flattening and CSV generation all run in the page in your browser, and there is no network request at any point. XML exports frequently contain customer records, prices or internal identifiers, which is a good reason to check where any converter processes your file.
What if some records have fields that others do not?
Every field found anywhere becomes a column, and records that lack it get an empty cell rather than being dropped or shifted. The report names those columns and says how many records are missing each one, because that is usually the reason an import later complains about unexpected nulls.
Does it handle namespaces, CDATA and entities?
Yes. Namespace prefixes are kept as part of the column name, which is what you want in a spreadsheet. CDATA sections are read literally. The five predefined XML entities and numeric character references are resolved. An entity the document never defines is left exactly as written rather than guessed at, so you can see it.
Can it convert CSV back to XML?
No, and that is deliberate. Going the other way means inventing a structure that the CSV does not contain, and the result is rarely the shape anyone actually wanted. Convert text or delimited data to CSV with our Text to CSV converter instead.
Last updated: September 7, 2026