1. ストリーミングパース入門
Java では、歴史的に XML を扱う 2 つのアプローチが存在します.
ご存じのとおり、DOM (Document Object Model) は文書のツリーをメモリ上に構築します。要素間を移動したり編集したりするのは便利ですが、大きなファイルではコストが高く、メモリをすぐに使い切ってしまいます。
一方で SAX (Simple API for XML) はファイルを順次処理し、タグに出会うたびにイベントを発行します。メモリ効率が良く、巨大な文書でも扱えます。しかしハンドラの実装が面倒で、構造をさかのぼることはできません。
StAX (Streaming API for XML) はその妥協案として登場しました。SAX と同じくストリーミングですが、開発者により多くのコントロールを与えます。必要なときに自分でストリームからイベントを引き出します。このアプローチは pull パース と呼ばれ、よりわかりやすく柔軟なコードが書けます。
StAX の概要
StAX (Streaming API for XML) は、JDK 6+ で導入された Java 向けの最新のストリーミング XML パーサーです。
主なアイデア: pull パース(「引っ張る」パーサー)。
SAX ではパーサーが自動的にあなたのメソッドを呼ぶ(push モデル)のに対し、StAX では自分で処理を制御します:
あなた自身がパーサーに「次のイベントを返して!」と要求します。
たとえ話:
SAX はテレビのようなもので、イベントが一方的に「流れて」きて、それに反応しなければなりません。
StAX は、準備ができたときに自分で「次の動画」を押して進めるサービスのようなものです。
StAX の主要クラス
StAX を扱うには、パッケージ javax.xml.stream の 2 つの主要クラスが必要です:
- 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 の読み出しを制御します:
- ストリームを開く(例: ファイル)。
- XMLStreamReader を作成する。
- ループで reader.next() を呼び出し、次のイベントを取得する。
- イベント種別を確認する(START_ELEMENT, END_ELEMENT, CHARACTERS など)。
- 目的の位置に達したら — データを処理する。
- パーサーを閉じる。
処理の流れ:
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 ファイルが非常に大きい(ギガバイト級)ため、全体をメモリに載せたくない。
- 文書の一部だけを処理したい(たとえば特定の要素を見つけたら停止)。
- シンプルでわかりやすいコードが欲しい: StAX は SAX より簡単で、大量のハンドラ実装が不要。
ユースケースの例:
- 大規模なデータ XML の取り込み(例: 1C のエクスポート、銀行明細、商品カタログ)。
- 必要な要素だけの検索・処理(例: 100 万レコードから <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();
}
}
利点:
- ファイル全体をメモリに読み込まない。
- 本が 100 万冊あっても処理でき、プログラムは「落ちない」。
6. StAX でありがちなミス
エラー1: パーサーやストリームを閉じ忘れる。 XMLStreamReader とストリーム(InputStream)は必ず閉じて、リソースリークを防ぎましょう。
エラー2: イベント種別を確認しない。 すべてのイベントが要素の開始や終了とは限りません。イベント種別を確認しないと、空文字列を拾ったり必要なデータを見落としたりします。
エラー3: 要素の入れ子を考慮しない。 XML の構造が複雑な場合(例: セクション内に書籍がある)、現在のネストレベルを追跡して要素を取り違えないようにしましょう。
エラー4: 大きなファイルに DOM を使う。 ファイルが大きい場合に DOM を使うと、OutOfMemoryError を招くことがあります。大きなファイルには StAX または SAX を使いましょう。
エラー5: 例外を処理しない。 ファイルや XML の処理では例外(XMLStreamException, IOException)が発生し得ます。適切に処理するか、上位へスローしてください。
GO TO FULL VERSION