Turn XML records into an XML to CSV table
Most XML exports are lists in disguise: a catalog of <book> elements, a feed of <order> entries, a set of <row> records from a legacy system. This XML to CSV converter finds that repeating element, turns every occurrence into a row, and turns attributes and child elements into columns, so you can open the data in any spreadsheet or load it into a database.
Example
<catalog>
<book id="bk101" lang="en">
<author>Gambardella, Matthew</author>
<title>XML Developer's Guide</title>
<price>44.95</price>
</book>
<book id="bk102" lang="en">
<author>Ralls, Kim</author>
<title>Midnight Rain</title>
<price>5.95</price>
</book>
</catalog>
Output:
@id,@lang,author,title,price
bk101,en,"Gambardella, Matthew",XML Developer's Guide,44.95
bk102,en,"Ralls, Kim",Midnight Rain,5.95
Author names contain a comma, so they are quoted automatically; everything else is written as is. The XML declaration, comments and the <catalog> wrapper do not appear in the table because they are not records.
How the rows are found
The converter parses the XML into a data tree, then walks down through wrapper elements that contain only one thing (such as <catalog>) until it reaches a list of repeated elements. Each element in that list becomes a row. Columns are collected from all rows in the order they first appear, so if only some books have a <discount> element, the column still exists and the other rows leave it empty.
Columns from attributes and nested elements
- Attributes become columns named with the Attribute prefix,
@idby default. Clear the prefix to get plainidheaders. - Nested elements are flattened with dots when Flatten nested objects is on:
<address><city>becomes anaddress.citycolumn. - Repeated child elements get numbered columns, like
sku.0andsku.1. - With flattening off, nested content is written into one cell as JSON text.
A pitfall: one child versus many
XML does not mark which elements are lists. Consider two orders where the first has one <sku> and the second has two:
<orders>
<order id="1"><sku>A</sku></order>
<order id="2"><sku>B</sku><sku>C</sku></order>
</orders>
@id,sku,sku.0,sku.1
1,A,,
2,,B,C
The single sku lands in its own column while the repeated ones are numbered. If your data mixes single and repeated children like this, merge the columns in your spreadsheet, or convert with flattening off to keep each order's SKUs together in one cell.
Delimiter, quoting and typing
- Delimiter – comma, semicolon, pipe or tab. Semicolons are often easier for spreadsheet apps in regions that use a decimal comma.
- Quote all fields – wraps every value in quotes, which some strict importers prefer.
- Detect numbers & booleans – mainly matters for other output formats; in CSV every value is text anyway, but leading zeros are always preserved.
- Include header row – turn it off to append rows to an existing file.
Related tools
Need a real workbook instead of text? Use XML to Excel. To inspect the structure before converting, try the XML viewer, and to check the CSV afterwards, the CSV viewer shows it as a table.
Privacy
The XML is converted in your browser and not uploaded. Only Load URL and Save & Share send content to our server.
Convert XML to CSV in Python
Parse the document with xml.etree.ElementTree, loop over the repeating element and write one row per record with csv.writer. Attributes are read with .get() and child elements with .findtext(). The common crash is record.find("price").text on a record that has no price: find() returns None and the script stops with an AttributeError. findtext("price", "") returns an empty string instead, which keeps the columns aligned.
import csv
import sys
import xml.etree.ElementTree as ET
data = """<catalog>
<book id="b1"><title>XML Guide</title><price>29.99</price></book>
<book id="b2"><title>Tea & Code</title></book>
</catalog>"""
root = ET.fromstring(data)
writer = csv.writer(sys.stdout, lineterminator="\n")
writer.writerow(["id", "title", "price"])
for book in root.iter("book"):
writer.writerow([book.get("id"), book.findtext("title", ""), book.findtext("price", "")])
Paste the XML into the converter above to see which columns it finds before you hard-code them in a script.