今回は、前回定義した 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 を用いてプル・パースを行う手順は概ね次の通り:
- XMLEventParser オブジェクトの取得
- XMLEventIterator オブジェクトの取得
- イベントの列挙
- 各イベントに対する処理
- 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 のインスタンスを直接生成している分、簡単かな?