CodeGym /課程 /JAVA 25 SELF /StAX (Streaming API for XML) — 流式解析

StAX (Streaming API for XML) — 流式解析

JAVA 25 SELF
等級 47 , 課堂 2
開放

1. 認識流式解析

在 Java 中,處理 XML 歷來有兩種主要方法。

我們已知道,DOM (Document Object Model) 會在記憶體中建立整棵文件樹。這樣瀏覽與編輯元素都很方便,但對大型檔案而言成本太高:記憶體很快就會耗盡。

相對地,SAX (Simple API for XML) 會順序處理檔案,遇到標籤時觸發事件。它非常省記憶體,能處理巨大的文件。但需要撰寫許多事件處理器,而且無法在結構中回到先前位置。

StAX (Streaming API for XML) 是一種折衷。它同樣是串流式(像 SAX),但給開發者更多掌控:需要時由我們自己「拉取」事件。這種方式稱為 pull 解析,能寫出更直觀且彈性的程式碼。

StAX 簡介

StAX (Streaming API for XML) 是 Java 的現代串流式 XML 解析器,於 JDK 6+ 引入。
核心概念:pull 解析(「拉取式」解析器)。

與 SAX 不同,在 SAX 中解析器會主動呼叫你的方法(push 模型);而在 StAX 中,你自行控制過程:
你主動向解析器請求:「給我下一個事件!」

類比:
SAX 像是電視廣播:事件不停向你湧來,你得即時回應。
StAX 像是服務中,你準備好時自己按「下一個影片」。

StAX 的關鍵類別

要使用 StAX,你需要套件 javax.xml.stream 中兩個主要類別:

  • XMLInputFactory — 建立解析器的工廠。
  • XMLStreamReader — 串流式解析器,以「一小段一小段」讀取 XML。

基本範例:

import javax.xml.stream.*;
import java.io.FileInputStream;

XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("data.xml"));

while (reader.hasNext()) {
    int event = reader.next();
    // 處理事件
}
reader.close();

2. StAX 的運作原理:pull 模型

在 StAX 中,你自行掌控 XML 的讀取:

  1. 開啟串流(例如檔案)。
  2. 建立 XMLStreamReader
  3. 在迴圈中呼叫 reader.next() 取得下一個事件。
  4. 檢查事件型別(START_ELEMENTEND_ELEMENTCHARACTERS,等等)。
  5. 到達需要的位置時——處理資料。
  6. 關閉解析器。

工作流程:

flowchart TD
    A[開啟 XMLStreamReader] --> B{hasNext?}
    B -- 是 --> C["next()"]
    C --> D{事件型別?}
    D -- START_ELEMENT --> E[處理元素開始]
    D -- CHARACTERS --> F[處理文字]
    D -- END_ELEMENT --> G[處理元素結束]
    D -- END_DOCUMENT --> H[結束]
    B -- 否 --> H

有什麼好處?

  • 你可自行決定何時讀取下一個元素。
  • 可以在需要的位置「停下來」,只處理檔案的一部分。
  • 不必像 SAX 一樣寫一堆處理器。

3. StAX 的事件型別

當你呼叫 reader.next() 時,解析器會回傳事件型別——一個整數(介面 XMLStreamConstants 的常數)。主要事件型別如下:

  • START_ELEMENT — XML 元素開始(<tag>)。
  • END_ELEMENT — XML 元素結束(</tag>)。
  • CHARACTERS — 標籤之間的文字內容。
  • END_DOCUMENT — 文件結束。

事件處理範例:

while (reader.hasNext()) {
    int event = reader.next();
    switch (event) {
        case XMLStreamConstants.START_ELEMENT:
            String name = reader.getLocalName();
            System.out.println("元素開始: " + name);
            break;
        case XMLStreamConstants.CHARACTERS:
            String text = reader.getText().trim();
            if (!text.isEmpty()) {
                System.out.println("文字: " + text);
            }
            break;
        case XMLStreamConstants.END_ELEMENT:
            System.out.println("元素結束: " + reader.getLocalName());
            break;
    }
}

4. 實用細節

何時使用 StAX?

StAX 是理想選擇,若符合以下情況:

  • XML 檔非常大(GB 級),且不想整個載入記憶體。
  • 只需處理文件的一部分(例如找到特定元素就停下)。
  • 需要簡潔易懂的程式碼:StAX 比 SAX 更簡單,且不必寫一堆處理器。

範例任務:

  • 匯入大型 XML 資料檔(例如 1C 的匯出、銀行對帳單、商品目錄)。
  • 只搜尋並處理需要的元素(例如,從百萬筆資料中只處理 <transaction>)。
  • 「即時」轉換 XML(例如過濾、彙整)。

DOM、SAX 與 StAX 的比較

方法 記憶體 易用度 彈性 適用情況
DOM 高(全部載入記憶體) 非常簡單 可修改樹 小/中型檔,且需要編輯 XML 時
SAX 最低 較難(事件處理器) 唯讀,無法回溯 超大型檔,簡單處理
StAX 最低 中等(pull 模型) 可分段讀取,易於停下 大型檔,需要兼顧彈性與簡單

StAX 是折衷的中庸之道:
— 不像 DOM 那樣耗記憶體。
— 不需要像 SAX 那樣撰寫複雜處理器。
— 允許你掌控解析流程。

5. 範例:用 StAX 讀取大型 XML 檔

假設我們有檔案 "books.xml"

<library>
    <book>
        <title>Java 入門</title>
        <author>Ivan Ivanov</author>
    </book>
    <book>
        <title>進階 Java</title>
        <author>Pyotr Petrov</author>
    </book>
    <!-- ... 許多書 ... -->
</library>

任務: 輸出所有書名。

StAX 程式碼:

import javax.xml.stream.*;
import java.io.*;

public class StaxDemo {
    public static void main(String[] args) throws Exception {
        XMLInputFactory factory = XMLInputFactory.newInstance();
        XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("books.xml"));

        while (reader.hasNext()) {
            int event = reader.next();
            if (event == XMLStreamConstants.START_ELEMENT && "title".equals(reader.getLocalName())) {
                reader.next(); // 前進到 CHARACTERS
                System.out.println("書名: " + reader.getText());
            }
        }
        reader.close();
    }
}

優點:

  • 不需要把整個檔案載入記憶體。
  • 就算有上百萬本書,程式也不會「當機」。

6. 使用 StAX 的常見錯誤

錯誤 1:忘了關閉解析器或串流。 務必關閉 XMLStreamReader 與串流(InputStream),避免資源洩漏。

錯誤 2:沒有檢查事件型別。 並非所有事件都是元素的開始或結束。請檢查事件型別,否則可能得到空字串或錯過需要的資料。

錯誤 3:沒有考慮元素的巢狀層級。 若 XML 結構複雜(例如分章節的書籍),請追蹤目前的巢狀層級,避免混淆元素。

錯誤 4:對大型檔使用 DOM。 若檔案很大——不要用 DOM,否則可能拋出 OutOfMemoryError。處理大型檔請用 StAX 或 SAX。

錯誤 5:沒有處理例外。 檔案與 XML 操作可能拋出例外(XMLStreamExceptionIOException)。請妥善處理或往上拋出。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION