実際のプログラムでは、各種の設定情報を変更したうえで、ファイルに書き戻すというような処理も考えられます。このような処理についても見ておきましょう。
[1] 設定値の更新
まず値を変更する処理です。いろいろなやり方が考えられますが、ここでは値をいったんノードにセットし直したうえで、ファイルに出力するという形で処理を進めてみます。
まずはノードに値をセットします(リスト8)。
NodeList params = connection.getChildNodes(); for (int i = 0; i < params.getLength(); i++) { Node node = params.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { String tag = node.getNodeName(); Node value = node.getFirstChild(); if (tag.equals("driver")) { value.setNodeValue(driver); } else if (tag.equals("url")) { value.setNodeValue(url); } else if (tag.equals("user")) { value.setNodeValue(user); } else if (tag.equals("password")) { value.setNodeValue(password); } else if (tag.equals("property")) { connection.removeChild(node); } } }
リスト7の処理とほぼ対照対象になっているので、すぐにお分かりいただけるかと思います。driver、url、user、passwordの各ノードについて、setNodeValueを実行しているだけです。
ここで少し注意が必要なのはpropertyの取り扱いです。propertyのノードは複数存在する可能性があるため、対応をチェックする必要があります。しかし、プロパティの数自体が増えている可能性も考えられるので単純にノードの値をセットし直すだけでは不十分です。
そこで、取りあえずここではpropertyのノードをいったん削除し、後であらためてノードを追加し直しています。
Enumeration keys = props.keys(); while (keys.hasMoreElements()) { String propname = (String) keys.nextElement(); String propvalue = props.getProperty(propname); // property のノードを生成 Element prop = root.createElement("property"); // 属性のノードを生成 Attr name = root.createAttribute("name"); Attr value = root.createAttribute("value"); name.setNodeValue(propname); value.setNodeValue(propvalue); // 属性を property のノードにセット prop.getAttributes().setNamedItem(name); prop.getAttributes().setNamedItem(value); // property のノードをセット connection.appendChild(prop); }
子ノードの追加については前回確認済みです。まずDocumentのノードを用い、ElementおよびAttrのノードを生成します。Elementが追加するpropertyのノードになり、Attrがname属性とvalue属性のノードになります。
NodeのgetAttributes()は、属性のノードの一覧であるNamedNodeMapを返します。このNamedNodeMapのsetNamedItem()を呼び出して先ほど作成した2つの属性のノードを追加しています。
最後に、作成したElementのノードをappendChildで追加して完了です(リスト9)。単純な操作ですが、少し長いので注意して対応を確認してみてください。
[2]ファイルへの出力
いまのところJAXPにはDOMをファイルに書き戻すという機能は用意されていません(コラム参照)。そこで、ノードを1つ1つたどってファイルに書き出す処理を作成します(リスト10)。
void serializeNode(Node node, PrintWriter out) throws IOException { switch (node.getNodeType()) { case Node.ELEMENT_NODE: String tag = node.getNodeName(); Attr attrs = node.getAttributes(); out.print("<" + tag); for (int i = 0; i < attrs.getLength(); i++) { Attr attr = (Attr) attrs.item(i); out.print(" "); out.print(attr.getNodeName()); out.print("="); out.print("\""); // 本来は文字をエスケープする必要あり out.print(attr.getNodeValue()); out.print("\""); } out.print(">"); NodeList children = node.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { serializeNode(children.item(i), out); } out.print("</" + tag + ">"); break; case Node.TEXT_NODE: // 本来は文字をエスケープする必要あり out.print(node.getNodeValue()); break; } }
処理の概要は以下のようになっています。
サンプルということで、XML宣言の出力や文字のエスケープなどの詳細な処理は省略してありますが、基本的にはこのような処理でよいでしょう。
前回の記事が掲載されてからしばらく後に、JAXPの新しいバージョンである1.1がリリースされました。今回の目玉はなんといってもXSLTサポートが追加されたことです。
JAXP 1.0でXMLパーサ生成のインターフェイスが統一されたように、JAXP 1.1では、XSLTエンジンの生成および呼び出しのインターフェイスが統一されました。
また、変換の一形態として、ツリーからストリームへのシリアライズもサポートされています。今回のサンプルではツリーからXMLのファイルへの出力処理を自前で記述しましたが、JAXP 1.1を用いれば、そのようなコードを記述する必要はありません。
現在のところ完全に対応しているものはリファレンス実装だけのようですが、The Apache XML Projectの手によるXalanではすでにその一部が実装されつつあります。
DOMプログラミングの実例として、XMLのファイルから設定情報を取り出すサンプルを取り上げて解説しました。
今回のようにJDBCの接続設定のみをファイルに定義するようなケースは少ないかもしれません。しかし、もっと一般的な場合でもルートノードの位置が変わるだけですので、今回の例はそのまま利用できるはずです。これを参考に、ぜひ実際のプログラミングに応用してみてください。
2回にわたって解説してきたDOMプログラミングですが、一応今回で終わりです。次回からはもう1つの標準APIであるSAXの基本について取り上げていく予定です。
それでは皆さん、次回までごきげんよう!
Copyright © ITmedia, Inc. All Rights Reserved.