倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

3. プル・パース

今回は、前回定義した XMLEvent を用いて、プル・パースを行う API を考えます。

XMLEventIterator インターフェースとその実装


まず、XMLEvent を列挙するインターフェース XMLEventIterator を定義しましょう。 StAX ではこの役割を XMLEventReader, XMLStreamReader 自体が行っていましたが、ここでは XMLEventParser とは別に列挙用のインターフェースを定義するのでした。 このインターフェースは XMLEventReader と同様に、java.util.Iterator インターフェースを拡張することにしましょう:

package xmlparsing;

import java.util.Iterator;
import xmlparsing.events.XMLEvent;

public interface XMLEventIterator extends Iterator<XMLEvent> {
    void close();
}

簡単のため、このインターフェースに新たなメソッドを定義していません。 ただし、このインターフェースには入力ストリームなどを閉じる close() メソッドのみ定義しています。 他にも、XMLEventIterator のように nextTag() や getElementText() のようなメソッドを定義しても構いません(というか、定義しないと何かと使いにくいでしょう)。

この XMLEventIterator の実装は、例えば javax.xml.stream.XMLStreamReader を用いて次のような感じにします:

package xmlparsing;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.Source;

import xmlparsing.events.*;

class StaxXMLEventIterator implements XMLEventIterator, XMLStreamConstants {
    
    private final XMLStreamReader reader;
    
    public StaxXMLEventIterator(Source source){
        try{
            this.reader = XMLInputFactory.newInstance().createXMLStreamReader(source);
            
        }catch(XMLStreamException ex){
            throw new RuntimeException(ex);
        }
    }

    public boolean hasNext() {
        try{
            return this.reader.hasNext();
            
        }catch(XMLStreamException ex){
            throw new RuntimeException(ex);
        }
    }

    public XMLEvent next() {
        try{
            switch(this.reader.next()){
                case START_DOCUMENT:
                    return new StartDocument();
                    
                case END_DOCUMENT:
                    return new EndDocument();
                    
                case START_ELEMENT:
                    return new StartElement(this.reader.getLocalName());
                    
                case END_ELEMENT:
                    return new EndElement(this.reader.getLocalName());
                    
                case CHARACTERS:
                    return new Text(this.reader.getText());
                    
                default :
                    throw new IllegalArgumentException(
                                "Unknown XML event appears : "+this.reader.toString());
            }
        }catch(XMLStreamException ex){
            throw new RuntimeException(ex);
        }
    }

    public void remove() {
        throw new UnsupportedOperationException();
    }

    public void close() {
        try{
            this.reader.close();
            
        }catch(XMLStreamException ex){
            throw new RuntimeException(ex);
        }
    }
}

あまり細かい実装や例外処理は気にしないようにしましょう。

XMLEventParser#pullparse() メソッド


次は XMLEventParser でプル・パースを行うメソッド pullparser() の実装です。 といっても、重要な実装は上記の XMLEventIterator, StaxXMLEventIterator に押し込まれているので、pullparse() メソッドの実装は簡単です:

package xmlparsing;

import javax.xml.transform.Source;
import xmlparsing.events.*;

public class XMLEventParser {

    /** プッシュ・パース */
    public void pushparse(Source source, XMLEventHandler handler){...}
    
    /** プル・パース */
    public XMLEventIterator pullparse(Source source){
        return new StaxXMLEventIterator(source);
    }
}

ただ単に StaxXMLEventIterator のインスタンスを生成して返しているだけです。

XMLEventParser の使用例


XMLEventParser を用いてプル・パースを行う手順は概ね次の通り:

  1. XMLEventParser オブジェクトの取得
  2. XMLEventIterator オブジェクトの取得
  3. イベントの列挙
  4. 各イベントに対する処理
  5. XMLEventIterator を閉じる

具体的な Java コードは以下のようになります(ここでは "sample.xml" というファイルを解析しています):

XMLEventParser parser = new XMLEventParser();  // (1) XMLEventParser オブジェクトの取得

Source src = new StreamSource("sample.xml");
XMLEventIterator iterator = parser.pullparse(src);  // (2) XMLEventIterator オブジェクトの取得

while(iterator.hasNext()){  // (3) イベントの列挙
    XMLEvent event = iterator.next();
    System.out.println(event.toString());  // (4) 各イベントに対する処理
}

iterator.close();  // (5) XMLEventIterator を閉じる

まぁ使い方に関しては StAX と大差ないので、難しくはないと思います。 XMLEventParser のインスタンスを直接生成している分、簡単かな?