XMLを使ってみる(その5)

50分の3

ActionScript 3 がイケてる50の理由 : Media Technology Labs (MTL)」というblogを読んで、「fifty reasons ActionScript 3 kicks ass」というスライドを知りました。
ActionScript3が素晴らしい具体的な理由を50個あげているのですが、その#36から#38がE4Xの紹介に割り当てられていました。
ちょっとそのスライドを見ながらをE4Xの扱い方をおさらいしつつメモしたいと思います。

#36: E4X: Inline XML, with variables

var myXML:XML =
  <xml>
    <item id="20" varAtt={myVar} />
    <item>{value}</item>
    {myOtherXML}
  </xml>;

XMLを使ってみる(その2)」で紹介した「XMLリテラルの中で式を評価」という機能の紹介ですね。
XMLに自由に変数を組み込めるのがとても便利、という話。
上記のXMLの例で言えば、例えば、このとき、

var myVar:int=2008;
var value:String="Memo String";
var myOtherXML:XML=<item id="21" varAttr={myVar+12} />;

という変数が事前に設定してあれば、trace( myXML.toXMLString() ) の出力結果は次のような内容になります。

[trace] <xml>
[trace]   <item id="20" varAtt="2008"/>
[trace]   <item>Memo String</item>
[trace]   <item id="21" varAttr="2020"/>
[trace] </xml>

#37: E4X: Powerful filtering

// get all item nodes
// where id attribute == val:
myXML..item.(@id == val)
// supports RegExp:
myXML..item.(/^Mc/.test(@name))

myXML..ってなんだ?これはタダのタイプミスですね、多分。(2008-07-16 タイプミスではありませんでした。myXML..itemというのはmyXML以下のitemノードを再帰的に取得したXMLListになるのですが、このサンプルでは1階層しかないのであまり意味がありません。)
まず1つめですが、アトリビュートで簡単にフィルタリングしてXMLListを取得できるので便利、という話。以下のコードで試してみます。

var itemXML:XML =
  <xml>
    <item id="1" name="one" />
    <item id="1" name="two" />
    <item id="1" name="three" />
    <item id="2" name="four" />
  </xml>;
var pickedItems:XMLList	= itemXML.item.(@id==1);
trace( pickedItems.toXMLString() );

以上のコードを実行すると次のように出力されます。

[trace] <item id="1" name="one"/>
[trace] <item id="1" name="two"/>
[trace] <item id="1" name="three"/>

ただし、例えば のように検索するアトリビュートが存在しない項目がある場合には以下のように例外が発生するのが問題です。この問題への対策は、try..catchで処理というよりも、forループの中でifを使った方が良さそうな気がします。

[フォルト] exception、情報 =ReferenceError: Error #1065:
 変数 @id は定義されていません。
 理由 : Fault、関数名 : test4()、場所 : TestXML.as:46
 46  var pickedItems:XMLList = itemXML.item.(@id==1);

2つめは、アトリビュートのフィルタリングに正規表現も使える、という話ですね。
先ほどの例のpickedItemsを選び出すところを次のコードに置き換えて実行してみます。

var pickedItems:XMLList	= itemXML.item.(/o/.test(@name));

nameアトリビュートにoが入っている項目を抜き出す、という処理なのですが、出力は以下のようになり意図通りにピックアップされました。

[trace] <item id="1" name="one"/>
[trace] <item id="1" name="two"/>
[trace] <item id="2" name="four"/>

ただし、この場合にもnameアトリビュートがないitem XMLオブジェクトがあると例外が発生します。

#38: E4X: XML manipulation

// append a new child node:
myXML.* += <item id='13' />
// remove child node with id 12:
delete(myXML.item.(@id == 12)[0])
// insert before second node:
myXML.*[1] = <item/> + myXML.*[1]

まず、一つめ。
myXML.* で表されるトップノードの子ノードリストに追加することになるので、子ノードの最後に追加されます。以下のコードで確認してみます。

var itemXML:XML =
  <xml>
    <item id="1" name="one" />
    <item id="1" name="two" />
    <item id="2" name="four" />
  </xml>;
itemXML.* += <item id='13' />;
trace( itemXML.toXMLString() );

// ついでに、itemXML.* が何ものであるのかを出力.
trace( getQualifiedClassName( itemXML.* ) );

次のように出力され、最後に

[trace] <xml>
[trace]   <item id="1" name="one"/>
[trace]   <item id="1" name="two"/>
[trace]   <item id="2" name="four"/>
[trace]   <item id="13"/>
[trace] </xml>
[trace] XMLList

なるほど、何となく仕組みが分かってきたので、続いて次のコードを試してみます。

// アトリビュートが一致した一つめを削除.
delete( itemXML.item.(@id == 1)[0]);
trace( "check -- 1\n" + itemXML.toXMLString() );

// 置換.
itemXML.*[1] = <item id='7' />
trace( "check -- 2\n" + itemXML.toXMLString() );

// [1]の後に挿入.
itemXML.*[1] += <item id='8' />
trace( "check -- 3\n" + itemXML.toXMLString() );

これらは次のように出力されます。

[trace] check -- 1
[trace] <xml>
[trace]   <item id="1" name="two"/>
[trace]   <item id="2" name="four"/>
[trace]   <item id="13"/>
[trace] </xml>
[trace] check -- 2
[trace] <xml>
[trace]   <item id="1" name="two"/>
[trace]   <item id="7"/>
[trace]   <item id="13"/>
[trace] </xml>
[trace] check -- 3
[trace] <xml>
[trace]   <item id="1" name="two"/>
[trace]   <item id="7"/>
[trace]   <item id="8"/>
[trace]   <item id="13"/>
[trace] </xml>

E4Xって、演算子でいろいろな操作ができ、かなり自由度が高いことをやれる仕組みですね。超便利!これは一度使ったら従来の(ActionScript2.0以前の)XML操作には戻れそうにありません。