倭マン's BLOG

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

XMLStreamReader をもう少し詳しく (5):要素の名前に関するメソッド

今回は要素の名前に関連するメソッドを見ていきます。 具体的には以下の4つのメソッドです:

  • getName()
  • getPrefix()
  • getLocalName()
  • getNamespaceURI()

これらは「要素の名前」に関連するメソッドのため、現在の XML イベントが属性 (Attribute) のときに呼び出しても属性の名前等を取得することはできないようです*1

メソッドの概要


JavaDoc に載っている各メソッドの仕様は下表のようになります:

メソッド名
:返り値
説明 許されるイベント 現在が許されないイベント
だった場合の挙動
getName()
QName
現在の要素の QName を返す START_ELEMENT
END_ELEMENT
IllegalStateException を投げる
getPrefix()
:String
接頭辞があればそれを返す START_ELEMENT
END_ELEMENT
null を返す
getLocalName()
:String
ローカル名もしくは実体参照名を返す START_ELEMENT
END_ELEMENT
ENTITY_REFERENCE
IllegalStateException を投げる
getNamespaceURI()
:String
要素名の名前空間 URI を返す START_ELEMENT
END_ELEMENT*2
null を返す

現在が許されないイベントだった場合に、「例外 (IllegalStateException) を投げる」か「null を返す」かが異なっている理由は、いまいちよく分かりません。 というより、Sun の JDK 実装では getLocalName() 等の場合も null を返したりします。 まぁ、null を返す方が扱いやすそうだし、どちらでもあまり困った記憶もありませんが。

また、getPrefix() メソッドの JavaDoc には

現在のイベントの接頭辞を返す。 またはイベントが接頭辞を持たなければ null を返す。
Returns the prefix of the current event or null if the event does not have a prefix

と書かれていますが、「名前空間(宣言)ノード」は接頭辞を持っているのか持っていないのかが判断付かないところ。 getPrefix() メソッドは(XMLStreamReader のクラス JavaDoc によると) START_ELEMENT, END_ELEMENT でしか呼び出せず、NAMESPACE では呼び出せないようなので、おそらく null が返されるかと思います。

実装例


上記のメソッドの実装例を2つほど。

  • QName を保持する実装
  • 接頭辞、ローカル名、名前空間 URI を保持する実装

の2つの場合にサンプルコードの一部を書いてみました↓

QName を保持する実装

QName を変数として保持する場合、面倒なのが getLocalName() で返される実体参照です。 これは仕様のせいなので仕方なし。 サンプルコードでは変数の初期化は書いてません:

    private QName qname;
    private String entityName;

    public QName getName(){
        switch(getEventType()){
            case START_ELEMENT:
            case END_ELEMENT:
                return this.qname;
            default:
                throw new IllegalStateException();
        }
    }

    public String getPrefix(){
        switch(getEventType()){
            case START_ELEMENT:
            case END_ELEMENT:
                return this.qname.getPrefix();
            default:
                return null;
        }
    }

    public String getLocalName(){
        switch(getEventType()){
            case START_ELEMENT:
            case END_ELEMENT:
                return this.qname.getLocalPart();
            case ENTITY_REFERENCE:
                return this.entityName;
            default:
                throw new IllegalStateException();
        }
    }

    public String getNamespaceURI(){
        switch(getEventType()){
            case START_ELEMENT:
            case END_ELEMENT:
                return this.qname.getNamespaceURI();
            default:
                return null;
        }
    }

接頭辞、ローカル名、名前空間 URI を保持する実装

つぎは接頭辞、ローカル名、名前空間 URI をそれぞれ String フィールドとして保持する実装。 こちらも変数の初期化は書いてません:

    private String prefix;
    private String localName;
    private String uri;

    public QName getName(){
        switch(getEventType()){
            case START_ELEMENT:
            case END_ELEMENT:
                return new QName(this.uri, this.localName, this.prefix);
            default:
                throw new IllegalStateException();
        }
    }

    public String getPrefix(){
        switch(getEventType()){
            case START_ELEMENT:
            case END_ELEMENT:
                return this.prefix;
            default:
                return null;
        }
    }

    public String getLocalName(){
        switch(getEventType()){
            case START_ELEMENT:
            case END_ELEMENT:
            case ENTITY_REFERENCE:
                return this.localName;
            default:
                throw new IllegalStateException();
        }
    }

    public String getNamespaceURI(){
        switch(getEventType()){
            case START_ELEMENT:
            case END_ELEMENT:
                return this.uri;
            default:
                return null;
        }
    }

*1:通常の XML 文書の解析では、現在のイベントが Attribute や Namespace となることはありません。

*2:XMLStreamReader のクラス JavaDoc に書かれている表を見ると、「getNamespaceURI() メソッドは全てのイベントで呼び出すことができる」と書かれていますが、これはおそらく 引数が String の getNamespaceURI(String) メソッドだけかと思います。 ただし、getNamespaceURI() をどの状態で呼び出しても例外は投げられませんが。