2009年6月アーカイブ

>>Flaファイルをダウンロード

とうとうこのエントリーを最後に、このサンプルともお別れですね。
そういえば、このサンプルの元ネタとも、今週でお別れですね。寂しいものです。

さて、前回はエレメントの生成・追加をやったのですが、その際にエレメントがダブってしまうのは、あんまりよろしくないぞ。
というわけで、「XMLクラスに、例えば指定エレメントのノードがあるかどうか調べるメソッドでもないかしら?」と思って探してみたのですが、残念ながらそんな便利なメソッドはありませんでした。

例えば
XMLデータ.何かしらのメソッド(@id == "なんか文字列");
で、Boolean型を返してくれると非常にありがたかったのですが・・・

しかし、無いもんは無い。って事で、他の処理で代用するしかありません。
要するに、指定した値での、XMLエレメントの有無チェックが出来ればいいのです。

というわけで実験。

if(test_xml.member.(name =="田井中 律") ){
    trace("エレメントがありますよ~ノシ");
}

 「エレメントがありますよ~ノシ」と出力されます。
しかし・・・

if(test_xml.member.(name =="ありえないエレメント") ){
    trace("エレメントがありますよ~ノシ");
}

「エレメントがありますよ~ノシ」と出力されます。
最悪だ・・・。

そこに、救世主が現れた。
その名も「toXMLString()メソッド

そもそもこのメソッドは、実行したXML オブジェクトのエレメントのストリング表現を返すメソッドです。
なので、例えば「test_xml.member.(name =="田井中 律").toXMLString()」を実行すると

<member part="Dram">
  <name>田井中 律</name>
</member>

上記の値を、XMLデータではなく、String値で返します。
大切なことなので2回言いますが、上記データは、XMLデータではなくStringデータです。

そして、上記のスクリプトを

if(test_xml.member.(name =="田井中 律").toXMLString() ){
    trace("エレメントがありますよ~ノシ");
}

if(test_xml.member.(name =="ありえないエレメント").toXMLString() ){
    trace("エレメントがありますよ~ノシ");
}

このように記述すれば、上のコードは、「エレメントがありますよ~ノシ」と出力され、下のコードは何も出力されません。else節があれば、そちらが実行されます。

てなわけで

前回のエントリーの、あずにゃん追加ボタン部分の関数を

/*----------------------------------------------
■addAzusaHandler
■概要
test_xmlにazunyan_xmlを追加する。
----------------------------------------------*/
function addAzusaHandler(event:MouseEvent):void{
    var azunyanName:String = azunyan_xml.name.toString();
   
    //test_xmlにあずにゃんがいたらreturnする。
    if(test_xml.member.(name == azunyanName).toXMLString() ){
        return;
    }
   
    test_xml.appendChild(azunyan_xml);
    inIt();
}

このように書き換えました。

var azunyanName:String = azunyan_xml.name.toString();

で、追加予定のXMLデータのnameエレメントの値を取得して、

if(test_xml.member.(name == azunyanName).toXMLString() ){
    return;
}

 上記処理で、当該memberエレメントが既に存在していたら、returnで、関数から抜けます。

つぎに俺も入部ボタン部分の関数を

/*----------------------------------------------
■addOreHandler
■概要
oreNameの値とorePartの値から、XMLノードを生成して
test_xmlに追加する。
----------------------------------------------*/
function addOreHandler(event:MouseEvent):void{
    var nameStr:String = oreName.text;
    var partStr:String = orePart.text;
   
    //テキストが空の場合return
    if(nameStr == "" || partStr == ""){
        return;
    }
   
    //名前・パートが被る場合return
    if(test_xml.member.(@part == partStr).(name == nameStr).toXMLString() ){
        return;
    }
   
    //入力内容から、XMLを生成する
    var addXML:XML = <member part={partStr}><name>{nameStr}</name></member>;
    test_xml.appendChild(addXML);
    inIt();
}

 こんな風に書き換えます。
こちらは、名前・パートの両方で抽出しています。一人の人間が複数パートを担当したり、ひとつのパートに複数担当がいたりってのはよくあるんで、Jazzとかオーケストラだと特に。

>>Flaファイルをダウンロード

しつこく、このサンプルを使います。

さて、次にプログラムからXMLエレメントを生成したり、XMLオブジェクトにエレメントを追加する方法を解説します。
これが出来ると、他のアプリケーションとのデータのやり取りの際に、統一された汎用フォーマットを利用できるので、開発が楽になります。

上記サンプルだと、「あずにゃん入部」ボタンと「俺も入部」ボタンになりますね。

あずにゃん入部ボタンは予め用意した下記のようなXMLデータを利用しています。

//あずにゃんのデータ
var azunyan_xml:XML =
<member part="Guitar">
    <name>中野 梓</name>
</member>;

 追加するボタンの処理は

test_xml.appendChild(azunyan_xml);

 このように、「XMLデータ.appendChild(追加するXMLデータ)」と記述します。
すると、「<houkagoTeaTime>」エレメントの末尾に、指定したXMLデータが追加されます。

その後、コンボボックスのデータプロバイダの設定処理を行えば、入部した部員やパートがコンボボックスに追加されます。

次に「俺も入部」ボタンで、入力データから生成したエレメントの挿入は

var nameStr:String = oreName.text;
var partStr:String = orePart.text;

//テキストが空の場合return
if(nameStr == "" || partStr == ""){
    return;
}

var addXML:XML = <member part={partStr}><name>{nameStr}</name></member>;
test_xml.appendChild(addXML);

 このように記述します。「oreName」「orePart」はそれぞれテキスト入力のテキストフィールドです。どちらかのテキストが入ってなかったら、returnによって、以降の処理を飛ばします。

キモになるのは、

var addXML:XML = <member part={partStr}><name>{nameStr}</name></member>;

この部分です。
ここで実際に入力データを格納した変数から、XMLエレメントを生成してるのですが、その際XML表現の中に変数を使う場合は「{変数名}」とします。

もし、{}を使用しなかった場合、「partStr」や「nameStr」は普通に文字列として扱われて、下記のようなXMLエレメントになってしまいます。気をつけましょう。

<member part=partStr>
    <name>nameStr</name>
</member>;

 これで、XMLデータの動的な生成が出来ましたが、問題が残ってます。
このままだと、あずにゃんが何人も何人も、桜高校軽音部に入部できてしまいます。

それはいけません。

なぜなら僕らの生きがいに、あずにゃんは一人だけです。だからこそ可愛いのです。

それ以外にも、例えばコンボボックスにデータを再設定する際に、「その1」のやり方のまんまだと、あずにゃんを追加した場合に、Guitarの選択肢が2つ入ってしまったりといった残念が発生します。

てなわけで、次回はエレメントの有無をチェックしたり、ユニークチェックしたりとかそういう処理を紹介します。

>>Flaファイルをダウンロード

今回も、このサンプルを使います。

前回のエントリーでコンボボックスに、それぞれ「nameエレメント」と、「part属性」をデータプロバイダとして設定しました。

コンボボックスの、どの値(セル)が選択されているかは「コンボボックスインスタンス.selectedItem」で参照できるので、

nameCmb.addEventListener(Event.CHANGE , nameSelectHandler);
/*----------------------------------------------
■nameSelectHandler
■概要
nameCmbから、エレメントを選択すると、選択されたエレメントをselectedTxtAreaに表示し
当該エレメントをsetected_xmlに代入する
----------------------------------------------*/
function nameSelectHandler(event:Event):void{
    var selectedNodeName:String = event.target.selectedItem.data.toString();
    setected_xml = new XMLList(test_xml.member.(name == selectedNodeName) );
    selectedTxtArea.text = setected_xml;
}

 コンボボックスの、選ばれているセルが変更された際に発行されるイベント「CHANGEイベント」で、新しく選ばれたセルが保持している「data」プロパティを取得した値により、「test_xml」から抽出しています。

「part属性」で抽出する場合は

setected_xml = new XMLList(test_xml.member.(@part == selectedNodepart) );

こうなります。

「ActionScript3.0でXML XMLの基本的な使い方」の応用ですね。

因みに、ひとつのパートに2人(ツインギター)や、一人の人間が複数パート(ギター&ボーカル)なんてケースも存在するため、抽出したデータは、XMLListクラスを利用します。
仮に、XMLクラスを使用した場合、抽出されたエレメントが単一ならば問題ないのですが、複数のエレメントが抽出された場合、ランタイムエラーが発生します。

>>Flaファイルをダウンロード

ActionScript 3.0でXMLを扱う仕組みが簡単になって凄く楽になりましたねぇ。

上のサンプルでは、一番右のテキストエリアに生XMLデータ、左のテキストエリアに、左側のコンボボックスから抽出されたエレメントのみを表示するようになっています。

さらに、新しいエレメントの追加や、ユーザーの入力から新たなノードを作る機能と、追加される際に、すでに登録されいるかどうかを調べて、2重登録を防ぐ機能を実装しています。

とはいえ、いきなり全機能を解説するのはアレなんで、何回かに分けて、ActionScript3.0でXMLを扱う仕組みをちょこっと解説してみようと思います。

まずはXMLを用意しましょう。

//テスト用XMLデータ
var test_xml:XML =
<houkagoTeaTime>
    <member part="Guitar">
        <name>平沢 唯</name>
    </member>
    <member part="Bass">
        <name>秋山 澪</name>
    </member>
    <member part="Keyboard">
        <name>琴吹 紬</name>
    </member>
    <member part="Dram">
        <name>田井中 律</name>
    </member>
</houkagoTeaTime>;

こんな感じでScriptを書きます。AS3.0では、コード内にもXMLを記述出来るんで、一々読み込む必要のないデータや、構造体を作って管理していた変数なんかは、今後XMLに統一するのもいいかもしれませんね。

さて、早速これをプログラム内で使ってみましょう。
てことで、左側のコンボボックスにXMLのデータから、値を抽出して、初期設定してみましょう。

import fl.data.DataProvider;
//コンボボックスのデータプロバイダ。
var nameItems:Array;

function setNameCmb(){
    nameItems = new Array();
   
    for each(var elements:Object in test_xml.member){
        nameItems.push({
                       label:elements.name.toString() ,
                       data:elements.name.toString()
                       });
    }
    nameCmb.dataProvider = new DataProvider(nameItems);
}

サンプルの、「名前で抽出のコンボボックス」の設定の記述です。
nameCmbは、コンボボックスのインスタンス名ですので、コードの最後の

nameCmb.dataProvider = new DataProvider(nameItems);

この記述で、コンボボックスに「XMLから抽出した値を構造体に格納した配列」を設定しています。

んで、実際にXMLに対して処理をしてるのが

for each(var elements:Object in test_xml.member)

になります。

for eachは、()内の「in」の後ろに記述された、XMLエレメントに対して、エレメント数分、繰り返し処理を行います。
このプログラムだと「test_xmlの、memberというエレメントの数分、繰り返し処理をしなさい」という意味で、さらに「in」の前 の「var elements:Object」は、処理の対象のエレメントの一時確保変数です。まぁ「for文」の「var i:int = 0」みたいなもんだと思って下さい。

なので、正しくは「test_xmlの、memberというエレメントの数分、elementsという変数名で、繰り返し処理をしなさい」となります。

コンボボックスのデータプロバイダは、「label」と「data」が入った構造体の配列を使用するので

nameItems.push({
           label:elements.name.toString() ,
           data:elements.name.toString()
           });

このようになります。
これで、たとえば「nameItems[0].label」とか「nameItems[2].data」とかで、値をとりだすことができるようになります。要するに、「label」と「data」という2つのデータを対にして、リスト的に扱えるようになるわけです。

なるほど、コンボボックスとは相性が良さそうです。

ActionScript3.0では、「タグ名.タグ名」という風に、「childNode」とか書かずに、直接タグ名でエレメントを操作できるので、

label:elements.name.toString()

これだけで、対象ノードのデータを取得することができます。
上の例だと、elementsは、for each の処理対象のtest_xml.memberエレメントなので、

test_xml.member[i].name.toString();

て な具合になりますね。ちなみに、複数のノードがある場合は、上のように「test_xml.member[5]」とかで、直接エレメントにアクセスすることが できるので、for each を使わなくても、通常のfor文使って、[i]でXMLエレメントを操作しても構いません。for eachを使った方が圧倒的に楽ですが・・・

何はともあれ、これで晴れてバンドメンバーの名前を抽出出来ました。

では次は担当パートを抽出してみたいところですが、こちらは「<member part="Bass">」のように、属性になっています。さてどうやってとりだそう・・・

AS2.0時代は、attributeなんてもんを使ってましたが、AS3.0からは

test_xml.member[i].@part.toString();

でOK。名前の前に「@」をつけるだけ。

なので、コンボボックスの「パートで抽出」は

partItems.push({
           label:elements.@part.toString() ,
           data:elements.@part.toString()
           });

これでOK。
ただし、ひとつのバンドに同じ楽器担当が複数いる場合もあるので(ツインギターとかツインボーカルとか)ユニークチェックをかけたりしてるんですが、それはまた次の機会に。

>>Flaファイルをダウンロード

ActionScript3.0のXMLの取り扱い その1.」の方を先に書いたあとで、そういえば、この話のさらに前提となる部分を多く省いてたなぁという事で、その辺を解説します。

//テスト用XMLデータ
var test_xml:XML =
<houkagoTeaTime>
    <member part="Guitar">
        <name>平沢 唯</name>
    </member>
    <member part="Bass">
        <name>秋山 澪</name>
    </member>
    <member part="Keyboard">
        <name>琴吹 紬</name>
    </member>
    <member part="Dram">
        <name>田井中 律</name>
    </member>
</houkagoTeaTime>;

こんなXMLデータを用意したとします。

そもそもAS3.0でXMLデータを扱う場合は

  1. XMLクラス
  2. XMLListクラス

を使います。

これらの違いは、詳しくはこちらのリンクにまかせるとして、簡単に説明すると。
XMLのタグで表わされる部分を、ノードだとかエレメントだとか呼んだりしますが、XMLクラスは、必ず一つのXMLエレメントで構成されている必要があります。

なので、複数のエレメントを同じレベルに並べることができません。

上記サンプルの例では、test_xmlは「<houkagoTeaTime>」という単一のエレメントで構成されています。

XMLListクラスは、複数のエレメントを扱います。
例えば

var testData:String =
<member part="Guitar">
    <name>平沢 唯</name>
</member>;

testData += <member part="Bass">
    <name>秋山 澪</name>
</member>;

testData += <member part="Keyboard">
    <name>琴吹 紬</name>
</member>;

testData += <member part="Dram">
    <name>田井中 律</name>
</member>;

こんなコードがあったとして、
testDataの値は

<member part="Guitar">
  <name>平沢 唯</name>
</member>
<member part="Bass">
  <name>秋山 澪</name>
</member>
<member part="Keyboard">
  <name>琴吹 紬</name>
</member>
<member part="Dram">
  <name>田井中 律</name>
</member>

このように、複数の「<member>」というエレメントで構成されたXML表現になります。
このString型の変数をXMLデータとして扱う場合。

var test_xml:XML = new XML(testData);

だと、コンパイルエラーになりますが

var test_xml:XMLList = new XMLList(testData);

という風に、XMLListクラスを利用すると、コンパイルエラーにはなりません。
ただし、複数のエレメントを持ったXMLListオブジェクトは、XMLクラスのメソッドを使う事が出来ないので、使いどころとしては、プログラム上で生成したXMLエレメントの一時作業用に、XMLListクラスを利用し、生成されたXMLListを、XMLクラスのオブジェクトと連結する。といったイメージでしょうか。

なので、基本的にはXMLクラスのオブジェクトに対して処理をします。

あとはエレメントの抽出方法ですが、
サンプルのXMLデータから値を抽出する場合の例をいくつか。

trace(test_xml.member);

の出力結果、すべての<member>エレメントを取得します。
<member part="Guitar">
  <name>平沢 唯</name>
</member>
<member part="Bass">
  <name>秋山 澪</name>
</member>
<member part="Keyboard">
  <name>琴吹 紬</name>
</member>
<member part="Dram">
  <name>田井中 律</name>
</member>

trace(test_xml.member.name);

の出力結果、すべての<member>の<name>エレメントを取得します。
<name>平沢 唯</name>
<name>秋山 澪</name>
<name>琴吹 紬</name>
<name>田井中 律</name>

trace(test_xml.member[0]);

の出力結果、指定したindexのエレメントを取得します。
<member part="Guitar">
  <name>平沢 唯</name>
</member>

trace(test_xml.member[0].name);

の出力結果、指定したindexのエレメントを取得します。
平沢 唯

trace(test_xml.member.@part);

属性を取得する場合、@を付けます。出力結果は
GuitarBassKeyboardDram

trace(test_xml.member[0].@part);

もちろんindexを指定することもできます。

trace(test_xml.member.(@part == "Dram") );

条件抽出する場合は上記のように記述。
出力結果は
<member part="Dram">
  <name>田井中 律</name>
</member>

trace(test_xml.member.(name == "秋山 澪") );

エレメントの値に対しても条件抽出できます。
出力結果は
<member part="Bass">
  <name>秋山 澪</name>
</member>

以上が、ActionScript3.0でXMLを扱う際の、ほんとのほんとに基礎部分です。
以下、用語紹介です。(Flashのヘルプから引用)

  • エレメント : XML ドキュメント内の単一アイテム。開始タグと終了タグ (タグも含みます) の間にあるコンテンツにより識別されます。 XML エレメントには、テキストデータ、その他のエレメントを含めることができます。また、空であってもかまいません。

  • 空のエレメント : 子エレメントを含まない XML エレメント。 空のエレメントは、自己終了タグ(<element/> など)として記述されることがよくあります。

  • ドキュメント : 単一の XML 構造。 XML ドキュメントには、任意の数のエレメントを含めることができます (また、単一の空のエレメントのみで構成することもできます)。ただし、XML ドキュメントには、ドキュメント内の他のすべてのエレメントを含む単一の最上位エレメントが存在する必要があります。

  • ノード : XML エレメントの別名。

  • 属性:エレメント内部にネストする個別の子エレメントとしてではなく、attributename="value" の形式でエレメントの開始タグに記述されるエレメントに関連する名前付き値。

プロフィール

HN.NoBody

NoBody

市ヶ谷のとあるオフィスでFlashクリエイターとして労役中。
なんとなくチーフ。

twitterでつぶやき中