XMLについてはリファレンスマニュアルの記述は大変限定的で役に立ちにくいです。
XPathの覚え書き#
XMLの読み書きについては既存のサンプルだとXMLドキュメントを上からスキャンして必要な部分を見つけ次第書き換えるというアルゴリズムが多いようです。
しかしYahoo!WidgetのXMLサービスによると、この煩雑なコーディングはXPathという関数群にによって軽減されるとあります。
まず次の様なXMLを想定します。MenuList.xmlと言う名前にします。
<?xml version="1.0" encoding="utf-8"?>
<menu>
<name>gaikyo</name>
<title>5_事業概況</title>
<filepath>5_事業概況.xls</filepath>
</menu>
<menu>
<name>chihou</name>
<title>6_地方税</title>
<filepath>6_地方税.xls</filepath>
</menu>
</xml>
やりたいことは、6_地方税の<filepath>の内容を"new.xls"に変更することです。XMLの中から<name>がchihouになっているものを見つけて、古い<filepath>を削除して新しく<filepath>(newFilePath)</filepath>を生成します。
var menudoc = XMLDOM.parse(filesystem.readFile("MenuList.xml"));
var newFilePath="new.xml";
var btnName="chihou";
var nodePath = "//main/menu[name='"+ btnName +"']";
var node=menudoc.evaluate(nodePath);
//古いレコードを削除する
var nodePath2 = "//main/menu[name='"+ btnName +"']/filepath";
var node2=menudoc.evaluate(nodePath2);
node.item(0).removeChild(node2.item(0));
//新しいものを追加する
var newElement =menudoc.createElement("filepath");
newElement.appendChild(menudoc.createTextNode(newFilePath));
node.item(0).appendChild(newElement);
txt=menudoc.toXML();
filesystem.write("file.txt",txt);
xpathの利点は、evaluate()関数を使うことで目的の行に一発でたどり着けることです。
ただし見つけ出してからどう加工するかは従来のDOMの処理方法が必要です。
リファレンスマニュアルによると
image = doc.evaluate("my-data/image-list/image[@size='48']");
src = image.item(0).getAttribute(src);
という文例になっているのですが、これの意味するところは、
<my-data><image-list> <image name="a" src="a.jpg" size="47"> <image name="b" src="b.jpg" size="48"> <image name="c" src="c.jpg" size="49"> </my-data></image-list>というデータ群の中からサイズが'48'のものを見つけ出して"b.jpg"というファイル名を得るというものです。
しかし、私が試したところでは@xxx=yyy という部分での検出がどうしてもできません。
Yahoo Widgetのフォーラムを見ても、"my-data/image-list を検出したら後は総当たりするというようなサンプルしかなく、<name>キーでさらに絞り込みをかけるという@...のサンプルが全くありません。
XPath使いのための日本語チートシート
を参考にすると、@=xx という書き方に限る必要はなく、私のケースでは
menudoc.evaluate("//main/menu[name='chihou']")
という書き方でよかったようです。evaluate()の内側はダブルクオートで囲み、その中でパスなどが出る場合はシングルクオートで囲みます。
先頭に//がつく場合はxmlの頂点からスキャンを行い、main/menu という書き方だと現在見ている行からスキャンするようです?
与えるパラメータの型が曖昧である場合は
node = menudoc.evaluate("string(//main/menu[name='chihou'])")
という書き方になるらしいです?stringをクオーテーションの内側に入れなければならないのが注意点です。stringというのもevaluate命令のほうのパラメーターです。よって、変数を与えるならシングルクオートを適切に使う必要があります。
見つかった変数の"node"というのは実のところ名前がよくありません。型が何なのかわからないので調べてみると
log(node.constructor);
>Constructor: function DOMNodeList()
DOMNodeListとなります。これが<menu>タグの個数だけ配列の様なものになっています。アクセスの仕方はnode[0]ではだめです。node.item(0)です。配列ではないということらしいです。
ここから要素を取り出そうとすると大問題です。 DOMNodelist の取説によると使えるファンクションは
DOMNode item(n)です。
そこにアクセスしたら得られる型は…
log(node.item(0);
>function DOMElement()
DOMElementだと返されました。
結局、帰ってくるのはDOMNode型であり、マニュアルは本当でウソなのはconstructor関数の結果の方でした。
あとはelement(<filepath>)を作り、</filepath>との間にTextNodeを挿入して先にevaluateで得たnode.item(0)の末尾にこれをくっつけると出来上がりです。
var newElement =menudoc.createElement("filepath");
newElement.appendChild(menudoc.createTextNode(newFilePath));
node.item(0).appendChild(newElement);
この基本操作をWidgetリファレンスマニュアルは全く解説していないので、敷居が高いと思う方は多いのではないでしょうか。
この項が参考になるとうれしいです。