1. 概述

本文将探讨使用Java将XML文件转换为CSV格式的多种方法。

XML(可扩展标记语言)和CSV(逗号分隔值)都是流行的数据交换格式。XML功能强大,适合处理复杂结构化数据;而CSV更简单直接,专为表格数据设计。

有时我们需要将XML转换为CSV以简化数据导入或分析流程。

2. XML数据布局简介

假设我们经营多家书店,库存数据以XML格式存储,示例如下:

<?xml version="1.0"?>
<Bookstores>
    <Bookstore id="S001">
        <Books>
            <Book id="B001" category="Fiction">
                <Title>Death and the Penguin</Title>
                <Author id="A001">Andrey Kurkov</Author>
                <Price>10.99</Price>
            </Book>
            <Book id="B002" category="Poetry">
                <Title>Kobzar</Title>
                <Author id="A002">Taras Shevchenko</Author>
                <Price>8.50</Price>
            </Book>
        </Books>
    </Bookstore>
    <Bookstore id="S002">
        <Books>
            <Book id="B003" category="Novel">
                <Title>Voroshilovgrad</Title>
                <Author id="A003">Serhiy Zhadan</Author>
                <Price>12.99</Price>
            </Book>
        </Books>
    </Bookstore>
</Bookstores>

此XML通过层次结构组织了属性(id/category)和文本元素(Title/Author/Price)。良好的XML结构能简化转换过程,减少错误。

我们的目标是将这些数据转换为CSV格式以便表格化处理。转换后的CSV应包含以下列:

  • 书店ID
  • 图书ID
  • 分类
  • 标题
  • 作者ID
  • 作者姓名
  • 价格

3. 使用XSLT转换

3.1. XSLT简介

XSLT(可扩展样式表语言转换)是一种将XML文件转换为其他格式(如HTML、纯文本或CSV)的工具。

它通过遵循XSL文件中的规则集工作,特别适合XML到CSV的转换场景。

3.2. XSLT转换过程

首先需要创建XSLT样式表,使用XPath导航XML树结构,定义XML元素到CSV行列的转换规则:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
    <xsl:template match="/">
        <xsl:text>bookstore_id,book_id,category,title,author_id,author_name,price</xsl:text>
        <xsl:text>&#xA</xsl:text>
        <xsl:for-each select="//Bookstore">
            <xsl:variable name="bookstore_id" select="@id"/>
            <xsl:for-each select="./Books/Book">
                <xsl:variable name="book_id" select="@id"/>
                <xsl:variable name="category" select="@category"/>
                <xsl:variable name="title" select="Title"/>
                <xsl:variable name="author_id" select="Author/@id"/>
                <xsl:variable name="author_name" select="Author"/>
                <xsl:variable name="price" select="Price"/>
                <xsl:value-of select="concat($bookstore_id, ',', $book_id, ',', $category, ',', $title, ',', $author_id, ',', $author_name, ',', $price)"/>
                <xsl:text>&#xA</xsl:text>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

关键点解析:

  • <xsl:template match="/"> 匹配XML根节点
  • <xsl:for-each select="//Bookstore"> 遍历所有书店节点
  • 内层循环处理每本书的数据
  • concat() 函数组合CSV行数据
  • &#xA 添加换行符

Java实现代码:

void convertXml2CsvXslt(String xslPath, String xmlPath, String csvPath) throws IOException, TransformerException {
    StreamSource styleSource = new StreamSource(new File(xslPath));
    Transformer transformer = TransformerFactory.newInstance()
      .newTransformer(styleSource);
    Source source = new StreamSource(new File(xmlPath));
    Result outputTarget = new StreamResult(new File(csvPath));
    transformer.transform(source, outputTarget);
}

优点:标准化、功能强大、适合中等规模数据
缺点:需将整个XML加载到内存,大文件处理吃力

4. 使用StAX

4.1. StAX简介

StAX(XML流式API)专为高效读写XML设计,支持流式处理,特别适合大文件处理。

转换过程包含三个核心步骤:

  1. 初始化StAX解析器
  2. 读取XML元素
  3. 写入CSV

4.2. StAX转换过程

完整实现示例:

void convertXml2CsvStax(String xmlFilePath, String csvFilePath) throws IOException, TransformerException {
    XMLInputFactory inputFactory = XMLInputFactory.newInstance();

    try (InputStream in = Files.newInputStream(Paths.get(xmlFilePath)); 
         BufferedWriter writer = new BufferedWriter(new FileWriter(csvFilePath))) {
        
        writer.write("bookstore_id,book_id,category,title,author_id,author_name,price\n");

        XMLStreamReader reader = inputFactory.createXMLStreamReader(in);

        String currentElement;
        StringBuilder csvRow = new StringBuilder();
        StringBuilder bookstoreInfo = new StringBuilder();

        while (reader.hasNext()) {
            int eventType = reader.next();

            switch (eventType) {
                case XMLStreamConstants.START_ELEMENT:
                    currentElement = reader.getLocalName();
                    if ("Bookstore".equals(currentElement)) {
                        bookstoreInfo.setLength(0);
                        bookstoreInfo.append(reader.getAttributeValue(null, "id"))
                          .append(",");
                    }
                    if ("Book".equals(currentElement)) {
                        csvRow.append(bookstoreInfo)
                          .append(reader.getAttributeValue(null, "id"))
                          .append(",")
                          .append(reader.getAttributeValue(null, "category"))
                          .append(",");
                    }
                    if ("Author".equals(currentElement)) {
                        csvRow.append(reader.getAttributeValue(null, "id"))
                          .append(",");
                    }
                    break;

                case XMLStreamConstants.CHARACTERS:
                    if (!reader.isWhiteSpace()) {
                        csvRow.append(reader.getText()
                          .trim())
                          .append(",");
                    }
                    break;

                case XMLStreamConstants.END_ELEMENT:
                    if ("Book".equals(reader.getLocalName())) {
                        csvRow.setLength(csvRow.length() - 1);
                        csvRow.append("\n");
                        writer.write(csvRow.toString());
                        csvRow.setLength(0);
                    }
                    break;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

核心逻辑:

  • 使用XMLStreamReader迭代XML事件
  • 通过事件类型(START_ELEMENT/CHARACTERS/END_ELEMENT)构建CSV行
  • StringBuilder高效拼接数据
  • 遇到</Book>时写入完整行

优点:内存高效,适合大文件/实时处理
缺点:需手动处理状态,转换能力不如XSLT

5. 其他方法

除了XSLT和StAX,还有其他可选方案:

  • DOM解析器:将整个XML加载到内存树,灵活但内存消耗大
  • SAX解析器:事件驱动,内存高效但状态管理复杂
  • Apache Commons CSV:简化CSV写入,但需自行处理XML解析

⚠️ 综合评估

  • DOM适合小文件但内存开销大
  • SAX状态管理繁琐
  • Apache Commons CSV需搭配XML解析器

结论:XSLT和StAX在大多数场景下提供了更平衡的解决方案。

6. 最佳实践

进行XML到CSV转换时需注意:

数据完整性

  • 验证XML模式(Schema)
  • 精确映射XML元素到CSV列

性能优化

  • 大文件使用流式处理(如StAX)
  • 考虑分批处理超大文件

⚠️ 特殊字符处理

  • 未处理逗号/换行符/双引号等特殊字符
  • 示例代码为简化未包含这些处理
  • 实际应用中需用双引号包裹含逗号的字段

踩坑提醒:生产环境务必处理特殊字符,否则可能导致CSV解析错误!

7. 总结

本文深入探讨了XML到CSV的转换方法,重点分析了XSLT和StAX两种方案。无论选择哪种方法,以下要素至关重要:

  • 设计适合CSV转换的XML结构
  • 实施数据验证机制
  • 正确处理特殊字符

根据实际场景选择合适方案:

  • 中小文件 → XSLT(开发效率高)
  • 大文件/实时流 → StAX(内存友好)

希望这些方法能助你高效完成XML到CSV的转换任务!


原始标题:Convert an XML File to CSV File | Baeldung