StAX - XmlParser
Java6から加わった標準XMLパーサにStAXというパーサーがあります。JavaのXMLパーサーといえば、SAXとDOMが有名ですが、StAXは第3のパーサーでSAX寄りの性質を持ちます。
DOMはXMLをパースした上で構造化した文書モデル(Document Object Model=DOM)へ変換をかけるAPIです。小粒なXMLであれば良いのですが、巨大なXMLの場合などどうしてもパフォーマンス面やリソース面が気になってしまいます。その代わりに非常に解り易いのですが。
逆にSAXは頭から読込み、タグの始まりや終わりなど、特定のタイミングでイベントが発生するタイプのAPIです。DOMに比べればパフォーマンス面もリソース面も優秀なのですが、今どのような状況でそのイベントが発生しているか?などの把握は実装者に委ねられるため、作りこむのに手間がかかります。
StAXはSAXの流れを組むAPIですが、イベントの発生や種別を解り易く書き換えている、と考えれば良いでしょう。
XMLStreamReaderとXMLEventReader
StAXではXMLStreamReaderとXMLEventReaderの2つのリーダが使用できます。内部的な動きはほとんど同じなのですが、EventReaderの方はイベントをEventオブジェクトとして扱える為、オブジェクト指向っぽくコーディングできるのが特徴です。一方、XMLStreamReaderでは発生するイベントは同じなのですが、イベントステータスを返すだけで、実際のタグやテキストはReaderから取得する感じになります。どちらでも大差はないですが、大きなパーサを構築するならばわかりやすいEventReader、速度重視だったりコンパクトなパーサであればXMLStreamReaderで良いのではないでしょうか?
実際にReaderを作成するには次のようにコードを書きます。
XMLInputFactory factory = XMLInputFactory.newInstance();
factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
XMLStreamReader reader = factory.createXMLStreamReader(input);
XMLInputFactoryクラスには同じようにXMLEventReaderを取得するメソッドもあります。
イベントの取得とループ
イベントの取得とループはこんなコードになります。
while (reader.hasNext()) { int c = reader.next(); switch (c) { case XMLStreamReader.START_ELEMENT: break; case XMLStreamReader.END_ELEMENT: reader.getName(); break; case XMLStreamReader.CHARACTERS: String text = reader.getText(); break; default: break; } }
IteratorのようなイメージでhasNextを使いループを記述します。next()を実行することで、イベントが発生するため、そのイベントごとに処理を記述していくだけになります。SAXよりも簡単に書けることが解るでしょう。尚、EventReaderの場合は、nextの代わりにnextEventを使い、イベントを発生させます。
改行コードがおかしい件
さて、問題がありまして、XMLStreamReader.CHARACTERSなどで取得したテキストのサイズがどうも一致しないケースが発生していました。調べてみると、どうも改行コードがすべてCRに変換されてしまっているようです。例えLFであってもです。CRLFの場合は文字数がずれる為、おかしな事にもなっていました。
結局はテキストを取得した後に、ファイルで使用されている改行コードに置換して対処をしましたが、バグなのでしょうかね。
String text = reader.getText(); text.replaceAll("\n", "\r\n");