倭マン's BLOG

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

ノードの変更と名前空間宣言 (1) -- 子要素に関するメソッド

今回から何回かに分けて、XML 文書のノード構成を変更する際に、名前空間がどのように扱われているかを見ていきましょう(一覧)。 今回は、要素に対して子要素を付加したり取り外したりするメソッドです:

  • Element#add(Element)
  • Element#detach()*1

ちなみに、Element#detach() メソッドは親要素に対して Element#remove(Element) メソッドを呼び出すのと同じです:

Element e = ...;

// これと・・・
e.detach();

// これは同じ処理。
Element parent = e.getParent();
parent.remove(e);

以降で各種メソッドを見ていきましょう。

Element#add(Element) メソッド


Element#add(Element) メソッドは、要素に子要素を付加するメソッドです。 このメソッドに関して名前空間の取り扱いで自明でないのは、子要素にする要素の名前がデフォルト名前空間に属している場合でしょう。

例えば、以下のようなサンプルを考えてみましょう:

Element parent = DocumentHelper.parseText("<parent xmlns='ns'/>")
                            .getRootElement();
Element child = DocumentHelper.parseText("<child/>")
                            .getRootElement();
        
parent.add(child);
System.out.println(parent.asXML());

これを実行すると

<parent xmlns="ns">
  <child xmlns=""/>
</parent>

となります(改行は勝手に付加しました)。 <child> 要素に新たな名前空間宣言が付加されています。

ただしこのサンプルでは、<child> 要素は「デフォルト名前空間」ではなく「NULL 名前空間」に属しています。 そもそも dom4j ではデフォルト名前空間は扱えません(たぶん)。 要素や属性の名前はインスタンス化した時点でローカル名と名前空間 URI のセットが固定されてしまいます。 そのため、ノードの構成の変更に伴って、それらの名前が属している名前空間が勝手に変更されたり、名前空間宣言が見つからなくなったりはしません。

ちなみに <child> 要素にある名前空間宣言「xmlns=""」は、実際には書き出しの際に付加されるもので、この要素に対応するオブジェクトには Namespace ノードとしては保持されていません。

Element#detach() メソッド


Element#detach() メソッドは対象要素を親要素から取り外します。 このメソッドに関して名前空間の取り扱いが自明でないのは以下の2つでしょう:

  1. 対象要素の名前がデフォルト名前空間に属している場合
  2. 対象要素が属している名前空間の宣言が親(祖先)要素にある場合
Element parent = DocumentHelper.parseText(
        "<parent xmlns='ns1' xmlns:p='ns2'>" +
            "<child1/>" +
            "<p:child2/>"+
        "</parent>").getRootElement();

// 1. 対象要素の名前がデフォルト名前空間に属している場合
Element child1 = parent.element("child1");
child1.detach();
System.out.println(child1.asXML());

// 2. 対象要素が属している名前空間の宣言が親(祖先)要素にある場合
Element child2 = parent.element("child2");
child2.detach();
System.out.println(child2.asXML());

この実行結果は以下のようになります(コメント、改行は勝手に付加しました):

<!-- 1. の場合 -->
<child1 xmlns="ns1"/>

<!-- 2. の場合 -->
<p:child2 xmlns:p="ns2"/>

どちらの場合も要素の名前が属している名前空間の宣言は付加されています*2

*1:実際には detach() メソッドは Node インターフェースに定義されているメソッドです。

*2:ただし、Element#add(Element) メソッドの場合と同様に 、親要素に Namespace ノードとして保持されてはいません。