倭マン's BLOG

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

XMLStreamReader をもう少し詳しく (4): getElementText() メソッド

今回は XMLStreamReader#getElementText() メソッドを見ていきます。

getElementText() メソッドは、要素の開始と要素の終了で挟まれたテキストを返します。 これは XML で書かれたプロパティファイルを処理する際に便利です。

例えば

<book>
  <title>なぜ宇宙人は地球に来ない?</title>
  <author>松尾貴史</author>
  <publisher>PHP新書</publisher>
</book>

という XML 文書があるとします。 「現在の XML イベント」が <title> 要素の開始の位置にある場合、getElementText() を呼び出すと文字列「なぜ宇宙人は地球に来ない?」が返されます。 呼び出し後の「現在の XML イベント」は <title> 要素の終了となります。

解析の際の処理は、現れた XML イベントの種類によって次の4種類に分類できます。 各 XML イベントに対する処理は以下のようになっています:

XML イベント 処理
CHARACTERS
CDATA
SPACE
ENTITY_REFERENCE
返すべき文字列に現在の文字列(getText() の返り値)を付加して次の XML イベントを処理する。
COMMENT
PROCESSING_INSTRUCTION
次の XML イベントを処理する。
END_ELEMENT 文字列を返して、getElementText() の解析を終了する
START_DOCUMENT
END_DOCUMENT
START_ELEMENT
ATTRIBUTE
NAMESPACE
DTD
ENTITY_DECLARATION
NOTATION_DECLARATION
XMLStreamException を投げる。

幾つか注意を:

  • getElementText() が呼ばれる直前には、現在のイベントは「要素の開始」でなければなりません(事前条件
  • getElementText() が呼ばれた直後では、現在のイベントは「要素の終了」でなければなりません(事後条件

実装例


XMLStreamReader#getElementText() の JavaDoc に載っている実装例(StringBuffer を StringBuilder に変えています):

    public String getElementText() throws XMLStreamException {

        if(getEventType() != START_ELEMENT) {
            throw new XMLStreamException(
                    "parser must be on START_ELEMENT to read next text", getLocation());
        }

        int eventType = next();
        StringBuilder content = new StringBuilder();

        while(eventType != END_ELEMENT) {
            if(eventType == CHARACTERS
                    || eventType == CDATA
                    || eventType == SPACE
                    || eventType == ENTITY_REFERENCE) {

                content.append(getText());

            } else if(eventType == PROCESSING_INSTRUCTION
                        || eventType == COMMENT) {
                // skipping

            } else if(eventType == END_DOCUMENT) {
                throw new XMLStreamException(
                        "unexpected end of document when reading element text content", 
                        getLocation());

            } else if(eventType == START_ELEMENT) {
                throw new XMLStreamException(
                        "element text content may not contain START_ELEMENT", getLocation());

            } else {
                throw new XMLStreamException(
                        "Unexpected event type "+eventType, getLocation());
            }

            eventType = next();
        }

        return content.toString();
    }

switch 文を使った実装例:

    public boolean isStartElement(){...}
    public int next(){...}
    public String getText(){...}
    public Location getLocation(){...}

    public String getElementText() throws XMLStreamException {
        if(!isStartElement()) 
            throw new XMLStreamException(
                    "現在のイベントは START_ELEMENT でなければなりません。", getLocation());

        StringBuilder content = new StringBuilder();
        
        while(true){
            int type = next();
        
            switch(type){
                case CHARACTERS:
                case CDATA:
                case SPACE:
                case ENTITY_REFERENCE:
                    content.append(getText());
                    continue;
                    
                case PROCESSING_INSTRUCTION:
                case COMMENT:
                    continue;
                    
                case END_ELEMENT:
                    return content.toString();
                
                case START_DOCUMENT:
                case END_DOCUMENT:
                case START_ELEMENT:
                case ATTRIBUTE:
                case NAMESPACE:
                case DTD:
                case ENTITY_DECLARATION:
                case NOTATION_DECLARATION:
                
                default:
                    throw new XMLStreamException(
                            "許されないイベントが現れました。", getLocation());
            }
        }
    }