xfactorstudioのXPath4AS2の使い方

「XPath」という記法自体は、XMLの各要素・属性・テキストにアクセスするための記法としてW3Cが勧告したものですが、このXPath記法をFlashでも使えるようにクラスメソッドを用意してくださったのが、xfactorstudioの「XPath4AS2(エックスパスフォーエイエスツー)」です。

サイト上にはAS1.0用の「XPath4AS」もありますが、今更使わないと思うので、ActionScript2.0に絞って「XPath4AS2」の使い方を書こうと思います。

内容としては、上記W3C勧告のXPathページの「2.5 Abbreviated Syntax」の章、そのページを日本語訳して下さった「どら猫本舗」様のページの内容を参考に、Flashで使えるか試したという感じです。

サンプルzipダウンロード(flashMX2004形式)
(XPathパッケージは含まれていませんので、別途下記より入手ください。)

前準備として、xfactorstudioのサイトからXPath4AS2をダウンロードさせていただき、解凍したフォルダ中のcomフォルダ以下をクラスパス登録(グローバルクラス登録するか、展開したcomフォルダと同じ階層で作業する)しておきます。(当サイトからxpathTestフォルダをダウンロードされた方は、そのフォルダの中にcomフォルダを置いてください。)

制作に入ります。まずは下記のようなxmlファイルを用意します。Shift-JISで作りがちですが、SEPYやFlashの事を考えて、UTF-8で作ります。下記のように文頭でUTF-8宣言した上で、保存時にUTF-8形式で保存して下さい。エディタは「秀丸」など、UTF-8対応エディタで作って下さい。

<?xml version="1.0" encoding="UTF-8"?>
<foods>
<food category="フルーツ">
<name>りんご</name>
<place>青森</place>
<price>100円</price>
</food>
<food category="フルーツ">
<name>みかん</name>
<place>愛媛</place>
<price>80円</price>
</food>
<food category="野菜">
<name>ゴーヤ</name>
<place>沖縄<city>那覇</city></place>
<price>70円</price>
</food>
<food>
<name>うどん</name>
<place>香川</place>
<price>100円</price>
<description>ネットで評判のおいしいうどんです。</description>
</food>
</foods>

上のXML文書をfoodData.xmlとして保存、同じ階層に新規でflaファイルを作成し、ルートの第1フレームに以下のように記述します。

import com.xfactorstudio.xml.xpath.*;

var foodXmlRoot:XML = new XML();
foodXmlRoot.ignoreWhite = true;
foodXmlRoot.onLoad = function(success:Boolean) {
if (success) {
//XMLのロードが成功したら、下に定義したxpathTest関数を実行します。
xpathTest();
} else {
trace("XMLのロードに失敗しました。");
}
};
foodXmlRoot.load("foodData.xml");

xpathクラスパッケージをインポートしておく以外は、普通のXMLロード作業です。phpとの連携の都合等で、どうしてもXML書類がShift-JISな時は、2行目にでも、

System.useCodepage=true;

と入力し、対応して下さい。

今回、XPathのテストは、XMLのロードが成功した時に実行するxpathTest関数の中で定義することにします。先程記述したフレームスクリプト(foodXmlRoot.load…)の下に、xpathTest関数を追加して下さい。XPathの使い方を勉強するための12個のテストを行っています。

//XMLオブジェクトインスタンスfoodXmlRootから、指定したデータを取得し、配列に格納するテスト。
function xpathTest(){
//test1:ノードを辿って、指定の要素にアクセスする。
var test1:Array=XPath.selectNodes(foodXmlRoot,"foods/food/name")
trace(test1);
//output:<name>りんご</name>,<name>みかん</name>,<name>ゴーヤ</name>,<name>うどん</name>

//test2:指定の要素中のテキストにアクセスする。
var test2:Array=XPath.selectNodes(foodXmlRoot,"foods/food/name/text()")
trace(test2);
//output:りんご,みかん,ゴーヤ,うどん

//test3:指定の要素の属性にアクセスする。
var test3:Array=XPath.selectNodes(foodXmlRoot,"foods/food/@category")
trace(test3);
//output:フルーツ,フルーツ,野菜

//test4:2番目のfood要素にアクセスする。
var test4:Array=XPath.selectNodes(foodXmlRoot,"foods/food[2]")
trace(test4);
//output:<food category="フルーツ"><name>みかん</name><place>愛媛</place><price>80</price></food>

//test5:最後のfood要素のnameに含まれるテキストにアクセスする。
var test5:Array=XPath.selectNodes(foodXmlRoot,"foods/food[last()]/name/text()")
trace(test5);
//output:うどん

//test6:XML中の全てのprice要素中のテキストにアクセスする。
var test6:Array=XPath.selectNodes(foodXmlRoot,"//price/text()")
trace(test6);
//output:100円,80円,70円,100円

//test7:「..」を使って、ノードをさかのぼっていくこともできます。このテスト自体は無意味ですが、、。
var test7:Array=XPath.selectNodes(foodXmlRoot,"foods/food[4]/name/../../foods/food[2]/name/text()")
trace(test7);
//output:みかん

//test8:food要素の中で、「フルーツ」属性を持つfoodの、name中のテキストにアクセスする。
var test8:Array=XPath.selectNodes(foodXmlRoot,"//food[@category='フルーツ']/name/text()")
trace(test8);
//output:りんご,みかん

//test9:food要素の中で、name要素が「うどん」の値を持つfoodの、price中のテキストにアクセスする。
var test9:Array=XPath.selectNodes(foodXmlRoot,"//food[name='うどん']/price/text()")
trace(test9);
//output:100円

//test10:属性が「フルーツ」のものの2番目の要素の名前にアクセスする。
var test10:Array=XPath.selectNodes(foodXmlRoot,"//food[@category='フルーツ'][2]/name/text()")
trace(test10);
//output:みかん

//test11:description要素を持つfood要素のnameの値にアクセスする。
var test11:Array=XPath.selectNodes(foodXmlRoot,"//food[description]/name/text()")
trace(test11);
//output:うどん

//test12:text()はテキストだけ、node()は内包する全てにアクセスできる。
var test12_1:Array=XPath.selectNodes(foodXmlRoot,"//food[3]/place/text()")
var test12_2:Array=XPath.selectNodes(foodXmlRoot,"//food[3]/place/node()")
trace(test12_1);
trace(test12_2);
//output test12_2:沖縄
//output test12_2:沖縄,<city>那覇</city>
}

重複してしまいますが、test1から解説していきます。

//test1:ノードを辿って、指定の要素にアクセスする。
var test1:Array=XPath.selectNodes(foodXmlRoot,"foods/food/name")
trace(test1);
//output:<name>りんご</name>,<name>みかん</name>,<name>ゴーヤ</name>,<name>うどん</name>

XPathを使う時はクラスメソッドのXPath.selectNodesを直接呼び出ます。返り値として該当した場所のデータを配列にして返します。式はそれぞれ

var 解析データ格納用Arrayインスタンス名:Array=XPath.selectNodes(解析対象のXMLオブジェクト,"解析対象の指定")

となります。text1の指定方法では<name>タグごと格納されていますね。そこで、

//test2:指定の要素中のテキストにアクセスする。
var test2:Array=XPath.selectNodes(foodXmlRoot,"foods/food/name/text()")
trace(test2);
//output:りんご,みかん,ゴーヤ,うどん

text()を使う事で、name要素の中のテキストだけを格納できます。こうやって取得した配列「test2」の各要素を取得したり、for文で回したりして使えばよいですね。test2[0]で「りんご」、test2[1]で「みかん」…となります。

//test3:指定の要素の属性にアクセスする。
var test3:Array=XPath.selectNodes(foodXmlRoot,"foods/food/@category")
trace(test3);
//output:フルーツ,フルーツ,野菜

「@」を使って、属性にもアクセスできます。category属性を設定したのは3カ所なので、3要素だけが出力されます。XMLツリー構造で見ると、foodタグ中に記述されたcategory属性ですが、指定文では「/@…」と、一見下の階層に思いがちなのでご注意ください。

//test4:2番目のfood要素にアクセスする。
var test4:Array=XPath.selectNodes(foodXmlRoot,"foods/food[2]")
trace(test4);
//output:<food category="フルーツ"><name>みかん</name><place>愛媛</place><price>80</price></food>

○番目の要素という指定もできます。ここで注意すべきなのは、xfactorstudioのXPath.selectNodesでは、1番目の要素は[0]ではなく[1]です。Flash標準のXPathやActionScript3のXMLListオブジェクトの指定方法とは異なるのでご注意ください。
test5のように、数字だけでなく、「最後のfood要素」という指定もできます。

//test6:XML中の全てのprice要素中のテキストにアクセスする。
var test6:Array=XPath.selectNodes(foodXmlRoot,"//price/text()")
trace(test6);
//output:100円,80円,70円,100円

これが大変ありがたいのです。XPath以前のFlashでは、firstChildやchildNodes等を使って、XMLのツリー構造を下っていく作業が面倒でしたが、XPathを使えば、「ツリー構造のどこに存在していようが、price要素を片っ端から配列に突っ込め」という指定ができるのです。これは便利です。
注意点としては、どの階層のprice要素でも取得するので、奥深い階層にたまたま別の使用目的でprice要素(同じ要素名)を作っていたら、そいつも取得し、予期せぬ結果・順番になります。でもまぁ、常識的にXMLを書いていれば、そんなことは滅多にないかと思います。

test7のように、「..」でツリー構造をさかのぼっていく指定もできます。どこで使うかはパッとはイメージできませんが。

//test8:food要素の中で、「フルーツ」属性を持つfoodの、name中のテキストにアクセスする。
var test8:Array=XPath.selectNodes(foodXmlRoot,"//food[@category='フルーツ']/name/text()")
trace(test8);
//output:りんご,みかん

//test9:food要素の中で、name要素が「うどん」の値を持つfoodの、price中のテキストにアクセスする。
var test9:Array=XPath.selectNodes(foodXmlRoot,"//food[name='うどん']/price/text()")
trace(test9);
//output:100円

//test10:属性が「フルーツ」のものの2番目の要素の名前にアクセスする。
var test10:Array=XPath.selectNodes(foodXmlRoot,"//food[@category='フルーツ'][2]/name/text()")
trace(test10);
//output:みかん

test8,9,10:このあたりもFlashではよく使うかと思います。ユーザーが「フルーツ」を選択したら〜という条件分岐に使えますね。要素中のテキスト、属性のどちらも条件にできます。文字列は「’」で囲って下さい

参考までに、food要素ににtaste属性を追加して

"//food[@category='フルーツ' and @tast='すっぱい']"

とか、

"//food[@category='フルーツ' and name='みかん']/price/text()"

みたいに、andを使って複数条件設定もできます。

//test11:description要素を持つfood要素のnameの値にアクセスする。
var test11:Array=XPath.selectNodes(foodXmlRoot,"//food[description]/name/text()")
trace(test11);
//output:うどん

のように、特定の要素を持つ親要素だけを探す事もできます。

//test12:text()はテキストだけ、node()は内包する全てにアクセスできる。
var test12_1:Array=XPath.selectNodes(foodXmlRoot,"//food[3]/place/text()")
var test12_2:Array=XPath.selectNodes(foodXmlRoot,"//food[3]/place/node()")
trace(test12_1);
trace(test12_2);
//output test12_2:沖縄
//output test12_2:沖縄,<city>那覇</city>

要素中にテキストと子要素が混在している時があれば、この違いを理解しておくとよいですね。

テストファイルをダウンロードして、ご自由にいじってみて、XPathの素晴らしさをご堪能ください。

追記:関数xpathTestの各test配列はローカル変数なので、他の場所から呼び出したい時はスコープをいじってください。


“xfactorstudioのXPath4AS2の使い方” への9件のフィードバック

  1. XPath4AS2の解説、ありがとうございました。
    とても役に立ちました。

    今回XPathを使用して1万行ほどのXMLを処理したところ、重くなりフリーズ状態になってしまったので限定的に使用することでなんとか回避しました。

  2. お役に立ててよかったです。
    >重くなりフリーズ状態
    そういえば元同僚の方もそのようなことを言っていました。
    膨大なデータを処理する際に、処理はしているのだけれど、画面の全てが止まった状態になるそうで、ローディング表示などができなくて困ったそうです。
    僕は実案件で使ったことはないのですが、あんまり使えないのですかねぇ。

  3. >> 膨大なデータを処理する際に、処理はしているのだけれど、画面の全てが止まった状態になるそうで、ローディング表示などができなくて困った

    そうなのです。その状態になってしまいました。
    時間がかかるのは問題ないのですが、FlashPlayerがフリーズ状態になってしまうので、大変でした。
    次バージョンに期待します。

  4. いつも拝見しています。
    このエントリー、わかりやすくて
    すごく役にたちました!ありがとうございます。

  5. >yamasaki様
    反映遅れてすいません。コメントありがとうございます。
    お役に立ててよかったです。
    yamasaki様のサイト拝見しました。すごい素敵ですねー。自分も「和」のテイストを考えたことがあったのですが、あんなに素敵なデザインはできませんでした。難しかったです。
    今後ともよろしくお願いいたします。

  6. アクションスクリプト初心者ですが、XPath4AS2の説明、わかりやすくてありがたく読ませていただいてます。ここに説明のあるところまではいけるのですが、function xpathTest()のなかで得た値をその関数の外で使用する方法がどうしてもわかりません。追記で「スコープをいじってください。」とある部分だと思うのですが、もしよろしければ、その辺りを少し解説いただけないでしょうか。

  7. ピンバック: 2009-12-11 « Black Box

コメントは受け付けていません。