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>
</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>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
关键点解析:
<xsl:template match="/">
匹配XML根节点<xsl:for-each select="//Bookstore">
遍历所有书店节点- 内层循环处理每本书的数据
concat()
函数组合CSV行数据

添加换行符
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设计,支持流式处理,特别适合大文件处理。
转换过程包含三个核心步骤:
- 初始化StAX解析器
- 读取XML元素
- 写入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的转换任务!