Skip to content
Format XML

Data Conversion

How to Convert XML to CSV

Five practical methods — from one-liners to full scripts — for turning hierarchical XML into flat CSV rows you can open in Excel, import into a database, or feed into a pipeline.

Why convert XML to CSV?

XML stores data as a nested tree of elements and attributes. CSV stores it as flat rows and columns. The tree is great for hierarchical relationships (a purchase order with line items, each with sub-items), but most analysis tools — Excel, Google Sheets, pandas, SQL databases — work best with flat tables.

Converting XML to CSV lets you import API responses into a spreadsheet, load feed data into a warehouse, or hand records to a colleague who does not have an XML parser.

Before converting, it helps to format your XML so you can see the element structure clearly. A well-indented document makes it obvious which elements repeat (those become rows) and which child elements or attributes become columns.

Step zero: understand the mapping

Not every XML document maps cleanly to a single CSV table. Before you pick a tool, answer two questions:

  1. Which repeating element is a “row”? In the example below, each <product> is a row.
  2. Which children/attributes are “columns”? Simple text children map one-to-one. Nested elements may need flattening (e.g. address/city) or splitting into a second CSV.

Here is the sample XML used throughout this guide:

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
  <product sku="A001">
    <name>Widget</name>
    <price currency="USD">9.99</price>
    <stock>142</stock>
  </product>
  <product sku="A002">
    <name>Gadget</name>
    <price currency="EUR">14.50</price>
    <stock>38</stock>
  </product>
</catalog>

Target CSV:

sku,name,price,currency,stock
A001,Widget,9.99,USD,142
A002,Gadget,14.50,EUR,38

Method 1: Python (xml.etree + csv)

Python ships with everything you need — no third-party packages required. The xml.etree.ElementTree module parses the XML, and the csv module writes properly escaped output.

import csv
import xml.etree.ElementTree as ET

tree = ET.parse("catalog.xml")
root = tree.getroot()

with open("catalog.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["sku", "name", "price", "currency", "stock"])

    for product in root.findall("product"):
        price_el = product.find("price")
        writer.writerow([
            product.get("sku"),
            product.findtext("name"),
            price_el.text,
            price_el.get("currency"),
            product.findtext("stock"),
        ])

When to use it: you need full control over which fields to extract, how to flatten nested elements, or how to handle missing values. Works on files of any size if you switch to iterparse for streaming.

Method 2: XSLT stylesheet

XSLT is XML’s own transformation language. You write a stylesheet that selects the nodes you want and outputs them as text:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" />

  <xsl:template match="/">
    <xsl:text>sku,name,price,currency,stock&#10;</xsl:text>
    <xsl:for-each select="catalog/product">
      <xsl:value-of select="@sku" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="name" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="price" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="price/@currency" />
      <xsl:text>,</xsl:text>
      <xsl:value-of select="stock" />
      <xsl:text>&#10;</xsl:text>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

Apply it with xsltproc catalog.xslt catalog.xml > catalog.csv on macOS/Linux, or with Saxon on any platform.

When to use it: the same XML structure arrives repeatedly (daily feeds, exports) and you want a reusable, versionable transform with no code dependencies.

Method 3: Command-line tools (xmlstarlet, xmllint + awk)

For a quick one-off extraction, xmlstarlet can select values with XPath and output them tab- or comma-separated:

xmlstarlet sel -T -t \
  -m "catalog/product" \
  -v "@sku" -o "," \
  -v "name" -o "," \
  -v "price" -o "," \
  -v "price/@currency" -o "," \
  -v "stock" -n \
  catalog.xml

If you only have xmllint (installed by default on macOS), you can use its --xpath flag to extract values one XPath at a time, then paste the results together with paste or awk. It is more awkward than xmlstarlet but works without installing anything.

When to use it: you are already in a terminal, the file is small, and you need the CSV in the next 30 seconds.

Method 4: Excel or Google Sheets

Both major spreadsheet apps can import XML directly:

  • Excel (desktop): File → Open → select the .xml file. Excel asks how to open it — choose “As an XML table.” The repeating elements become rows, child elements become columns, and you can then Save As CSV.
  • Google Sheets: use the IMPORTXML function with an XPath: =IMPORTXML("URL", "//product"). This works for publicly accessible XML files. For local files, paste the data and use Find & Replace or Apps Script to split it.

When to use it: you are not a developer and just need the data in a spreadsheet. For complex or nested XML, the auto-mapping may not produce the columns you expect — check the result.

Method 5: Online XML-to-CSV converters

Several websites let you paste XML and download CSV. They are convenient for small, non-sensitive files. Before using one, consider:

  • Privacy: most online converters upload your data to a server. If the XML contains PII, API keys, or internal records, prefer a local method.
  • Size limits: many cap input at 1-2 MB.
  • Column mapping: auto-detection works for flat XML but often mishandles attributes or deeply nested structures.

A good first step before any converter is to format and validate your XML to make sure it is well-formed. A syntax error that a converter silently ignores can produce rows with shifted columns and no warning.

Method comparison

MethodBest forHandles nestingSetup
PythonComplex or large filesFull controlPython installed
XSLTRepeating feedsXPath-basedxsltproc or Saxon
CLI (xmlstarlet)Quick one-offsXPath-basedPackage install
Excel / SheetsNon-developersAuto (limited)None
Online converterSmall, non-sensitive filesAuto (limited)None

Handling tricky XML structures

Real-world XML is rarely as clean as a tutorial sample. Here is how to deal with the common complications:

Missing or optional elements

If some <product> nodes lack a <stock> child, your code should output an empty cell rather than crashing. In Python, findtext("stock", "") returns an empty string when the element is absent.

Repeating child elements (one-to-many)

A product with multiple <tag> children does not fit into a single CSV column. Common solutions: join them with a delimiter (e.g. "electronics|gadgets"), create one row per child (duplicating parent data), or split into two CSV files with a foreign key.

Namespaces

If your XML uses namespaces, XPath queries need the namespace prefix or a wildcard. In Python: root.findall("{http://example.com}}product"). Forgetting this is the number-one reason “my XPath returns nothing.”

Commas and quotes in text content

If an element’s text contains commas or double quotes, you must quote the CSV field and escape inner quotes by doubling them. Python’s csv.writer handles this automatically. If you are building CSV strings manually (XSLT, shell), you need to add that logic yourself or switch to a TSV output to avoid the problem.

Format your XML first

Before converting, paste your XML into the formatter to indent it and catch syntax errors. A well-formed, readable document makes column mapping straightforward.

Open Format XML