ノード上の基本操作
さて、「ノードツリーの生成」までの手順で皆さんの手元にノードツリーの根元のオブジェクトが入手できているはずです。このルート(根)ノードのオブジェクトを足がかりにして、ノードツリーにアクセスしてみましょう。
ここでは、リスト1のサンプル文書を読み込んだという仮定で話を進めます。皆さんの手元にあるルートノードのオブジェクトは、図1でいうところのDocumentを指していることになります。
ノードの種類を確認
ここで手元にあるノードを確認しておきましょう。ノードのオブジェクトを取得した直後に、以下のようなコードを挿入してください。
if (root.getNodeType() == Node.DOCUMENT_NODE) { System.out.println("Root is Document!"); }
Node#getNodeType()は、ノードの種類を返すものです。ノードの種類はNodeに定数として定義されており、ここではDocumentノードかどうかをテストしています。
さて、これでとりあえずスタート地点は確認できました。では実際に子どものノードを取り出してみます。
子ノードへのアクセス
子どものノードを取り出すにはいくつかの方法があります。最初に説明するのはgetFirstChild()です。
Node child = root.getFirstChild();
getFirstChild()は、あるノードの直下にある子ノードのうち、最初の1つを返します。図1を見れば分かるようにルートの直下にはprofileタグのノードがあるはずです。
タグの名前を表示させて確認してみましょう。タグの名前を取得するには、getNodeName()を用います。
System.out.println(child.getNodeName());
さて、getFirstChild()は、直下の子ノードのうち、最初の1つを返すものでした。Documentの直下にはルートノード、つまりprofileが1つあるだけでしたからこれで問題ありませんが、一般的に子ノードは1つとは限りません。
そんなときに便利なのがgetChildNodes()です。これは複数の子ノードをまとめて取り出すためのものです。
NodeList gchildren = child.getChildNodes();
NodeList型は、ノードを格納するための可変長配列オブジェクト、いうなればNode専用のVectorだと考えてほぼ間違いありません。いま取り出したNodeListに、子ノードがすべて入っていることを確認してみましょう。
for (int i = 0; i < gchildren.getLength(); i++) { Node gchild = gchildren.item(i); System.out.println("Name: " + gchild.getNodeName()); }
このコードを見ればお分かりのように、NodeListに入っているノードの個数はgetLength()で確認でき、子ノード自体はitem()で取り出すことができます。さて、リスト2のような出力結果が確認できたでしょうか?
#text name #text gender #text job #text white #text
図1と見比べてみてください。ちゃんと対応がとれていることがお分かりいただけるかと思います。
テキストのノードの名前が#textになっていることに注意してください。これらのノードは実際にはTextノードで、実際のテキストの内容を格納しています。
このテキストの値こそ実際にアクセスすべきデータとなるものです。それらの値を実際に取り出してみましょう。とはいえ、gchildrenに入っているテキストノードは空のものばかりです。図1を見て、テキストが格納されていると思われるノードを探してみてください。
いくつかあると思いますが、ここではgenderノードの下にあるテキストを取り出してみることにしましょう。まず、そのノードを取り出します。
Node gender = gchildren.item(3).getFirstChild();
gchildrenの4番目の子ノード(genderタグのノード)をitem()で取り出し、その直下にあるテキストノードをgetFirstChild()で取り出しています。もちろんgetChildNodes()してからitem(0)で取り出しても構いません。少し複雑に感じるかもしれませんが、そのような場合は図1と上記の操作をよく見比べてみてください。
さて、苦心してテキストが格納されていると思われるノードを取り出したわけですから、実際のテキストを取り出してみましょう。
System.out.println(gender.getNodeValue());
図1に示すように、genderタグのノードの下にあるテキストが表示されていますか?
以上で、ノードにアクセスするための方法をだいたい見てきました。子ノードにアクセスするばかりで、逆に親ノードを取り出す方法についてはここでは触れませんでしたが、皆さんへの宿題にしておきましょう。APIドキュメントなどを見ればすぐに確認できますので、ぜひチェックしておいてください。
属性値へのアクセス
さて、1つ重要なことを忘れています。そう属性値ですね。属性値のノードは通常のノードと異なり、getAttributes()で取り出します。
NamedNodeMap attributes = child.getAttributes();
NamedNodeMapは、NodeListと同じくノードの集合を格納するためのコンテナです。個数(getLength())とノードの取得(item)の機能に加え、「ノード名を指定してノードを取り出す」機能が付加されています。
ここでいうノード名とは、getNodeName()で得られる名前のことです。通常のエレメントのノードではタグ名に相当するものでしたが、属性では属性名に対応します。では実際に属性名を指定してノードを取り出してみましょう。
Node id = attributes.getNamedItem("id"); System.out.println(id.getNodeName()); System.out.println(id.getNodeValue());
見てお分かりのとおり、getNamedItem()で属性名を指定して属性のノードを取り出しています。上記のコードを実行すればすぐに確認できると思いますが、ノードの値は属性の値に対応しています。
ノードツリーの変更
最後に、ノードに対して子ノードを追加したり、削除したりする機能について見ておきましょう。
まず子ノードを新たに追加してみます。あるノードに子ノードを追加するには、その名もappendChild()というメソッドが用意されていますからそれを用いてください。
Document doc = (Document) root; Node office = doc.createElement("office"); Node value = doc.createTextNode("Esaka, Osaka, Japan"); office.appendChild(value); child.appendChild(office);
新たに追加するノードを、Documentオブジェクトを用いて生成していることに注意してください。createElementにより指定したタグ名のElementオブジェクトを、createTextNodeにより指定したテキストを値として持つTextオブジェクトを生成します。
ここでは、勤務場所を示す<office>タグを追加しています。childノードの子ノード一覧を表示し、このノードが実際に追加されていることを確認してみてください。ここまで読み進められた皆さんなら、方法はもうすでにお分かりと思いますので、ここでは実例は省いておきます。ぜひ手を動かして確認してください。
実際に確認すると分かることですが、<office>タグは、子ノードの最後に追加されています。途中に挿入したい……という場合は、insertBefore()を用います。
Node favorite = doc.createElement("favorite"); favorite.appendChild(doc.createTextNode("Holmes")); child.insertBefore(favorite, office);
新しく<favorite>タグのノードを生成し、先ほど追加したofficeノードの前に挿入しています。今回はテキストノードを生成して直接代入していますが、操作の内容としては先ほどの例と同じです。この例の動作確認も皆さんの宿題としておきます。簡単ですので必ず確認しておいてください。
さて、今度は子ノードを削除してみましょう。ノードを取り除くにはNodeのremoveChildを用います。
child.removeChild(office);
removeChildは、引数に指定された子ノードのオブジェクトを親ノードから切り離します。ここでは先ほど追加したofficeのノードを指定しています。
さて、いままでのノード操作の例をすべて実行すると、ノードツリーの形は実際にはどのような形になっているのでしょうか? 操作をトレースしてみれば机上でも確認できますが、ここはぜひ実際にコードを書いて確認してみてください。
これを機会に、ノードを再帰的にたどって、ツリー全体の内容を表示するようなプログラムを書いてみるのもいいでしょう。今回紹介した機能だけで十分記述できますので、挑戦してみてください。
まとめと次回の予告
今回はDOMの操作の基本について簡単に解説してみました。以下に主なものを一覧の形で整理しておきます。
- ドキュメントビルダの生成とノードツリーの生成
- DocumentBuilderFactory#newInstance()
- DocumentBuilderFactory#newDocumentBuilder()
- DocumentBuilder#parse()
- 子ノードの操作
- Node#getChildNodes()
- Node#getNodeName()
- Node#getNodeValue()
- NodeList#getLength()
- NodeList#item()
- 属性の操作
- Node#getAttributes()
- NamedNodeMap#getNamedItem()
- ノードの追加と削除
- Document#createElement()
- Document#createTextNode()
- Node#appendChild()
- Node#insertBefore()
- Node#removeChild()
今回取り上げたのは、org.w3c.domパッケージに含まれる膨大なインターフェイスおよびそのメソッドのうちの一部です。今回紹介できなかったものにもいろいろと面白いものがありますので、興味がある方はぜひ仕様書やAPIドキュメントなどをあたってみてください。
次回は、今回学んだ機能を使って、実際の応用事例に挑戦してみることにします。
それでは皆さん、次回までごきげんよう!
Copyright © ITmedia, Inc. All Rights Reserved.