前回から,新しいXMLパーサであるStAX (Streaming API for XML)を紹介しています。

イベントイテレータAPI

前回はカーソルAPIについて解説しました。カーソルAPIではイテレータのようにXMLをパースしていくことができ,パースの制御をアプリケーションで行なうことが可能です。

しかし,イベントを番号で扱い,イベントによりパーサから取得できる情報が異なるなどの問題もあります。

それに対して,今回紹介するイベントイテレータAPIはイベントをクラスで表します。取得できる情報はイベントに内包されているため,取得できる情報を容易に判別することができます。

それでは,前回使用したサンプルをイベントイテレータAPIで書き直してみましょう。

サンプルのソース EventIteratorSample1.java

このサンプルも要素が開始されると,その要素名を出力します。

    public EventIteratorSample1(String xmlfile) {
        // 1. パーサ用ファクトリの生成
        XMLInputFactory factory = XMLInputFactory.newInstance();
 
        XMLEventReader reader = null;
        BufferedInputStream stream = null;
 
        try {
            // 2. 入力に使用するファイルの設定
            stream = new BufferedInputStream(
                         new FileInputStream(xmlfile));
 
            // 3. パーサの生成
            reader = factory.createXMLEventReader(stream);
 
            // 4. イベントループ
            while (reader.hasNext()) {
                // 4.1 イベントを取得
                XMLEvent event = reader.nextEvent();
                
                // 4.2 StartElementであれば名前を出力
                if (event.isStartElement()) {
                    StartElement element = (StartElement)event;
                    System.out.println("Name: " + element.getName());
                }
            }
        } catch (FileNotFoundException ex) {
            System.err.println(xmlfile + " が見つかりません");
        } catch (XMLStreamException ex) {
            System.err.println(xmlfile + " の読み込みに失敗しました");
        } finally {
            // 5. パーサ,ストリームのクローズ
            if (reader != null) {
                try {
                    reader.close();
                } catch (XMLStreamException ex) {}
            }
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException ex) {}
            }
        }
    }

前回のカーソルAPIの場合と異なる部分を赤で示しました。

パーサ用のファクトリはカーソルAPIと同じでXMLInputFactoryクラスを使用します。しかし,パーサはインタフェースが異なります。

カーソルAPIではjavax.xml.stream.XMLStreamReaderインタフェースでしたが,イベントイテレータAPIではjavax.xml.stream.XMLEventReaderインタフェースが使われます。

このため,パーサの生成もXMLInputFactoryクラスのcreateXMLEventReaderメソッドを使用します。このメソッドも引数がストリーム,リーダ,XSLTで使用されるソースが使用できます。

イベントループは,パーサのhasNextメソッドで制御されることは同じですが,イベントの取得は異なります。

カーソルAPIではイベントは番号で区別していましたが,イベントイテレータAPIではイベントオブジェクトを使用します。これに応じて,メソッドも異なります。カーソルAPIがnextメソッド,イベントイテレータAPIは4.1で使用しているようにnextEventメソッドです。

イベントイテレータAPIで使用するイベントは,すべてjavax.xml.stream.events.XMLEventインタフェースの派生インタフェースとなります。このため,nextEventメソッドの戻り値の型もXMLEventインタフェースになっています。

XMLEventインタフェースの派生イベントはjavax.xml.stream.eventsパッケージで定義されており,13種類あります。それぞれカーソルAPIのイベントに対応したインタフェース名なので,すぐ分かるはずです。

たとえば,要素の開始はStartElementインタフェースです。

ただし,カーソルAPIには存在したCDATAとSPACEは,Charactersインタフェースでまとめて表されます。

XMLEventインタフェースにはオブジェクトがどのイベントであるか調べるためのユーティリティメソッドが定義されています。要素の開始であれば,4.2で使用したようにisStartElementメソッドになります。

カーソルAPIではXMLStreamReaderオブジェクトから取得していた情報は,イベントイテレータAPIではイベントオブジェクトから所得するようにします。

たとえば,StartElementインタフェースにはgetNameメソッド,getNamespacesメソッドなどが定義されています。

そこで,4.2のif文の内部ではStartElementオブジェクトからgetNameメソッドをコールして,要素の名前を取得しています。

このようにイベントをオブジェクトで表すことにより,カーソルAPIよりもシンプルなコードを記述することが可能になります。

さて,このサンプルを実行してみましょう。

XMLドキュメントは前回と同じname.xmlを使用しました。

<?xml version="1.0" encoding="utf-8"?>
<names>
  <name>
    <first>Bob</first>
    <last>Dylan<last>
  </name>
  <name>
    <first>Paul</first>
    <last>Simon</last>
  </name>
  <name>
    <first>Pete</first>
    <last>Seeger</last>
  </name>
</names>

実行結果を次に示します。

C:\stax>java EventIteratorSample1 names.xml
Name: names
Name: name
Name: first
Name: last
Name: name
Name: first
Name: last
Name: name
Name: first
Name: last

names.xmlでは名前空間を使用していないので,要素名だけが出力されました。名前空間が指定してあれば,カーソルAPIと同様に名前空間も一緒に出力されます。