倭マン's BLOG

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

XMLStreamReader をもう少し詳しく (3): nextTag() メソッド

今回は XMLStreamReader#nextTag() メソッドの処理を見ていきます。

nextTag() メソッドは、次の要素の開始 (START_ELEMENT) または要素の終了 (END_ELEMENT) まで XML 文書の解析を進めます。 解析の際の処理は、現れた XML イベントの種類によって次の4種類に分類できます:

XML イベント 処理
START_ELEMENT
END_ELEMENT
nextTag() の解析を終了する。
CHARACTERS
CDATA
(文字データが)空白なら*1次の XML イベントを読み込む、そうでないなら XMLStreamException を投げる。
SPACE
COMMENT
PROCESSING_INSTRUCTION
次の XML イベントを読み込む。
START_DOCUMENT
END_DOCUMENT
ATTRIBUTE
NAMESPACE
DTD
ENTITY_REFERENCE
ENTITY_DECLARATION
NOTATION_DECLARATION
XMLStreamException を投げる。

幾つか注意を:

  • nextTag() メソッドは、他の nextXxxx() メソッドと同様、現在の XML イベントの次の XML イベントから処理を行います。 
  • nextTag() が正常に終了したならば、その時の XML イベントは START_ELEMENT もしくは END_ELEMENT になっています(事後条件)。
  • nextEvent() メソッドなどの状態遷移メソッドを何も呼び出してない状態で nextTag() メソッドを呼び出すと、(次の XML イベントが START_DOCUMENT なので)XMLStreamException が投げられます。
  • 現在の状態が END_DOCUMENT の場合、nextTag() メソッドを呼び出すと NoSuchElementException が投げられなければいけない(hasNext() == false のため)ですが、Sun の JDK 実装では XMLStreamException が投げられます。 まぁ、hasNext() メソッドによるチェックをきちんとすれば、あまり問題はありませんが。

実装例


実装例を2つ。 どちらも next() メソッドを使って必要な XML イベントまで解析を進めます。

まずは nextTag() の JavaDoc に載っていた実装:

    public int nextTag() throws XMLStreamException {
        int eventType = next();
        while((eventType == CHARACTERS && isWhiteSpace()) // skip whitespace
                || (eventType == CDATA && isWhiteSpace()) // skip whitespace
                || eventType == SPACE
                || eventType == PROCESSING_INSTRUCTION
                || eventType == COMMENT){

            eventType = next();
        }

        if (eventType != START_ELEMENT 
                && eventType != END_ELEMENT) {

            throw new XMLStreamException("expected start or end tag", getLocation());
        }

        return eventType;
    }

次は switch 文を使った実装。 現れた XML イベントごとにどういった処理を行うかが分かりやすいと思いますが、どうでしょう?

    public boolean hasNext(){...}
    public int next(){...}
    public int getEventType(){...}
    public boolean isWhiteSpace(){...}
    public Location getLocation(){...}

    public int nextTag() throws XMLStreamException {
        while(true){
            int type = next();
            
            switch(type){
                case START_ELEMENT:
                case END_ELEMENT:
                    return type;
                    
                case CHARACTERS:
                case CDATA:
                    if(isWhiteSpace())
                        continue;
                    else
                        throw new XMLStreamException(
                            "空白でない文字データが現れました。", getLocation());
                    
                case SPACE:
                case COMMENT:
                case PROCESSING_INSTRUCTION:
                    continue;
                    
                case START_DOCUMENT:
                case END_DOCUMENT:
                case ATTRIBUTE:
                case NAMESPACE:
                case DTD:
                case ENTITY_REFERENCE:
                case ENTITY_DECLARATION:
                case NOTATION_DECLARATION:
                    
                default:
                    throw new XMLStreamException(
                        "要素でなく空白でもないイベントが現れました。", getLocation());
            }
        }
    }

*1:「isWhiteSpace() == true」なら。