倭マン's BLOG

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

4. プッシュ・パース

プル・パースの次はプッシュ・パースを行う API を考えていきます。

前回までは単に StAX の簡略版を作ってただけです。 今回はそれを SAX っぽく扱おうってだけです。

XMLEventHandler インターフェースとそのデフォルト実装


プッシュ解析を行うには、SAX の org.xml.sax.ContentHandler のような、各イベントに対するコールバック処理を定義するインターフェースが必要です。 このインターフェースの各メソッドの引数に、前回までに作成した XMLEvent インターフェースとそのサブクラスを援用しましょう:

package xmlparsing;

import xmlparsing.events.*;

public interface XMLEventHandler {
    void startDocument(StartDocument event);
    void endDocument(EndDocument event);
    void startElement(StartElement event);
    void endElement(EndElement event);
    void text(Text event);
}

SAX の org.xml.sax.helpers.DefaultHandler と同じような、実装の際に用いるヘルパークラスも定義しておきましょう:

package xmlparsing;

import xmlparsing.events.*;

public class DefaultXMLEventHandler implements XMLEventHandler{
    public void startDocument(StartDocument event) {}
    public void endDocument(EndDocument event) {}
    public void startElement(StartElement event) {}
    public void endElement(EndElement event) {}
    public void text(Text event) {}
}

実際にプッシュ・パースを行う際には、このクラスのサブクラスを作成します。

XMLEventParser#pushparse() メソッド


XMLEventParser#pushparse() メソッドの実装例を見ていきましょう。 あくまで実装例。 ここではpullparse() メソッドの実装に pushparse() メソッドを用いています:

package xmlparsing;

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

public class XMLEventParser {

    /** プッシュ・パース */
    public void pushparse(Source source, XMLEventHandler handler){
        XMLEventIterator iterator = pullparse(source);
        
        while(iterator.hasNext()){
            XMLEvent event = iterator.next();
            switch(event.getEventType()){
                case XMLEvent.START_DOCUMENT:
                    handler.startDocument((StartDocument)event);
                    break;
                    
                case XMLEvent.END_DOCUMENT:
                    handler.endDocument((EndDocument)event);
                    break;
                    
                case XMLEvent.START_ELEMENT:
                    handler.startElement((StartElement)event);
                    break;
                    
                case XMLEvent.END_ELEMENT:
                    handler.endElement((EndElement)event);
                    break;
                    
                case XMLEvent.TEXT:
                    handler.text((Text)event);
                    break;
                    
                default:
                    throw new RuntimeException(
                            "Unknown XML Event appears : "+event.getEventType());
            }
        }

        iterator.close();
    }
    
    /** プル・パース */
    public XMLEventIterator pullparse(Source source){...}
}

XMLEventParser の使用例


ではプッシュ・パースの使用例を見ていきましょう。 まず、SAX と同じようにコールバック処理を定義するために、独自の XMLEventHandler オブジェクトを作成する必要があります。 必ずそうしなければいけないわけではありませんが、ここではDefaultXMLEventHandler クラスを拡張して XMLEventHandler オブジェクトを作成します:

public class MyEventHandler extends DefaultXMLEventHandler{

    @Override
    public void startDocument(StartDocument event) {
        System.out.println("<?xml version=\"1.0\"?>");
    }

    @Override
    public void endDocument(EndDocument event) {
        System.out.println("<!-- EOF -->");
    }

    @Override
    public void startElement(StartElement event) {
        System.out.println("<"+event.getName()+">");
    }

    @Override
    public void endElement(EndElement event) {
        System.out.println("</"+event.getName()+">");
    }

    @Override
    public void text(Text event) {
        System.out.println(event.getText());
    }
}

あとは以下の手順でプッシュ・パースを行います:

  1. XMLEventParser オブジェクトの取得
  2. XMLEventHandler オブジェクトの取得
  3. プッシュ・パースの実行

具体的な Java コードは次のようになります:

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

Source src = new StreamSource("sample.xml");        
XMLEventHandler handler = new MyEventHandler();  // (2) XMLEventHandler オブジェクトの取得
parser.pushparse(src, handler);  // (3) プッシュ・パースの実行

最初の方にも書きましたが、単にイベント・ハンドラに前回までに作った XMLEvent インターフェースとそのサブクラスを援用しているだけで、たいして難しいことはないかと思います。