將 XML 文件轉換為 CSV 文件
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」以及文字元素「標題」、「作者」和「價格」整齊地組織在層次結構中。確保結構良好的 XML 可以簡化轉換過程,使其更加簡單且無錯誤。
目標是將這些資料轉換為 CSV 格式,以便於以表格形式處理。為了說明這一點,讓我們來看看 XML 資料中的書店如何以 CSV 格式表示:
bookstore_id,book_id,category,title,author_id,author_name,price
S001,B001,Fiction,Death and the Penguin,A001,Andrey Kurkov,10.99
S001,B002,Poetry,Kobzar,A002,Taras Shevchenko,8.50
S002,B003,Novel,Voroshilovgrad,A003,Serhiy Zhadan,12.99
接下來,我們將討論實作這種轉換的方法。
3.使用XSLT轉換
3.1. XSLT 簡介
XSLT(可擴展樣式表語言轉換)是將 XML 檔案變更為各種其他格式(例如 HTML、純文本,甚至 CSV)的工具。
它按照特殊樣式表(通常是 XSL 檔案)中設定的規則進行操作。當我們旨在將 XML 轉換為 CSV 以便於使用時,這變得特別有用。
3.2. XSLT 轉換過程
首先,我們需要建立一個 XSLT 樣式表,它使用 XPath 來導覽 XML 樹結構並指定如何將 XML 元素轉換為 CSV 行和列。
以下是此類 XSLT 檔案的範例:
<?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>
此樣式表首先符合根元素,然後檢查每個「Bookstore」節點,收集其屬性和子元素。就像書本的id
、 category
等一樣,放入變數中。然後使用這些變數建立 CSV 檔案中的每一行。 CSV 將包含書店 ID、圖書 ID、類別、標題、作者 ID、作者姓名和價格列。
<xsl: template <xsl:template>
設定轉換規則。它使用<xsl:template match=”/”>
定位 XML 根,然後定義 CSV 標頭。
指令 <xsl:for-each select=”//Bookstore”> 處理每個“Bookstore”節點並捕獲其屬性。另一個內部指令<xsl:for-each select=”./Books/Book”>
處理目前「 Bookstore
」中的每個「 Book
」。
concat()
函數將這些值組合成一個 CSV 行。
新增換行(LF)字符,對應於十六進位表示法的 ASCII 值 0xA。
以下是我們如何使用基於 Java 的 XSLT 處理器:
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);
}
我們使用TransformerFactory
來編譯 XSLT 樣式表。然後,我們建立一個Transformer
對象,它負責將此樣式表應用於我們的 XML 數據,將其轉換為 CSV 檔案。程式碼運行成功後,指定目錄中會出現一個新檔案。
使用 XSLT 進行 XML 到 CSV 的轉換非常方便且靈活,為大多數用例提供了標準化且強大的方法,但需要將整個 XML 檔案載入到記憶體中。對於大檔案來說這可能是個缺點。雖然它非常適合中型資料集,但如果我們有更大的資料集,您可能需要考慮使用 StAX,我們接下來將介紹它。
4. 使用StAX
4.1. StAX簡介
StAX(XML 串流 API)旨在以更節省記憶體的方式讀寫 XML 檔案。它允許我們即時處理 XML 文檔,使其成為處理大文件的理想選擇。
使用 StAX 進行轉換涉及三個主要步驟。
- 初始化 StAX 解析器
- 讀取 XML 元素
- 寫入 CSV
4.2 StAX轉換過程
這是一個完整的範例,封裝在名為convertXml2CsvStax()
的方法中:
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();
}
}
首先,我們透過建立XMLInputFactory
的實例來初始化 StAX 解析器。然後我們使用這個工廠物件產生一個XMLStreamReader
:
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
InputStream in = new FileInputStream(xmlFilePath);
XMLStreamReader reader = inputFactory.createXMLStreamReader(in);
我們使用XMLStreamReader
迭代 XML 文件,並根據事件類型(例如START_ELEMENT, CHARACTERS,
和END_ELEMENT
)建立 CSV 行。
當我們讀取 XML 資料時,我們建立 CSV 行並使用BufferedWriter
將它們寫入輸出檔案。
簡而言之,StAX 提供了一種節省記憶體的解決方案,非常適合處理大型或即時 XML 檔案。雖然它可能需要更多的手動工作並且缺乏 XSLT 的一些轉換功能,但它在需要考慮資源利用率的特定場景中表現出色。有了所提供的基礎知識和範例,我們現在準備在適用這些特定條件時使用 StAX 來滿足 XML 到 CSV 的轉換需求。
5. 附加方法
我們主要關注 XSLT 和 StAX 作為 XML 到 CSV 的轉換方法。但是,還有其他選項,例如 DOM(文件物件模型)解析器、SAX(XML 簡單 API)解析器和 Apache Commons CSV。
然而,還有一些因素需要考慮。 DOM 解析器非常適合將整個 XML 檔案載入到記憶體中,使您能夠靈活地自由遍歷和操作 XML 樹。另一方面,當您需要將 XML 資料轉換為 CSV 格式時,它們確實會讓您更加努力。
當談到 SAX 解析器時,它們的記憶體效率更高,但可能會為複雜的操作帶來挑戰。它們的事件驅動特性要求您手動管理狀態,而且它們不提供在 XML 文件中向前或向後查看的選項,這使得某些轉換變得非常麻煩。
Apache Commons CSV 在編寫 CSV 檔案時表現出色,但希望您自行處理 XML 解析部分。
總而言之,雖然每個替代方案都有其自身的優點,但對於本例來說,XSLT 和 StAX 為大多數 XML 到 CSV 轉換任務提供了更平衡的解決方案。
6. 最佳實踐
要將 XML 轉換為 CSV,需要考慮資料完整性、效能和錯誤處理等多個因素。根據 XML 模式驗證 XML 對於確認資料結構至關重要。此外,將 XML 元素正確地對應到 CSV 欄位是一個基本步驟。
對於大文件,使用 StAX 等流技術可以提高記憶體效率。此外,請考慮將大檔案分解為較小的批次,以便於處理。
值得一提的是,所提供的程式碼範例可能無法處理 XML 資料中的特殊字符,包括但不限於逗號、換行符和雙引號。例如,欄位值中的逗號可能與 CSV 中用於分隔欄位的逗號衝突。同樣,換行符號可能會破壞檔案的邏輯結構。
解決此類問題可能很複雜,並且根據具體專案要求而有所不同。要解決逗號問題,您可以在產生的 CSV 檔案中將欄位括在雙引號中。也就是說,為了使本文中的程式碼範例易於理解,這些特殊情況尚未解決。因此,應該考慮到這一方面以獲得更準確的轉換。
七、結論
在本文中,我們探討了將 XML 轉換為 CSV 的各種方法,特別是深入研究了 XSLT 和 StAX 方法。無論選擇哪種方法,擁有適合 CSV 的 XML 結構、實施資料驗證以及了解要處理哪些特殊字元對於順利、成功的轉換至關重要。這些範例的程式碼可在 GitHub 上取得。