XMLを読もう

Flash CS3でのやりかたはAS3.0でXMLを使ってみるを参照してください。

XMLでユニコードですよ

 Flashは最新技術である。だからとーぜんXMLでユニコードなのである。
 と言うわけで、今回はXMLを読み込んでみよう。

 FlashのXML操作は5から実装されているが、どーも5でのXML操作は使えたもんではなかったようだ。
 MXでユニコードのXMLを読むようになった。ちなみに、改行文字はCRでもLFでもいいようなのだが、CRLF(Win式)だと改行2つと解釈するみたい。
 同じサブドメインのファイルしか読めないので、Flash単体で他のサイトの用意しているxmlを読んで、Flashでいろいろ表示するのは無理っぽい。
 間にCGIを噛ませると別だけどね。

 XMLそのものに付いては解説しない。面倒だから、というかFlashと直接関係ないし。
 jpegファイルを扱うときも、説明なしにjpegと使うように、xmlファイルも解説しない。あしからずんばずびずば、ぱぱぱやー。
 まぁ、いいや、XMLを読もう。

XMLオブジェクトに読み込もう

 Flashでは、XMLオブジェクトを作って、そこにxmlファイルを読み込むと言う感じである。
 読み込み終わったら、読み込んじゃったよーと言うイベントが発生し、指定したハンドラが起動する。
 なんでそんな回りくどいことをするかと言えば、Flashはアニメーションを表示するから、読み込むまで待っていると、アニメが止まっちゃう。コレはいかんね。と言うわけで、読む準備ができたらとりあえず次にいって、読み終わってから処理を続行すると言うわけだ。

 まず、ユニコードで適当なxmlを書いて、swfと同じフォルダに置く。
 そして、次のようなコードを最初のフレームに書いておく。

theXML = new XML();//XMLオブジェクトを作る
theXML.onLoad = XMLLoaded;//イベントハンドラ指定
theXML.load("test.xml");//xmlファイルを読み込む

function XMLLoaded() {
	trace(theXML);//出力ウインドウに表示
}

 はい、読めました。
 特に問題なし。

パースで引っかかる

 次は、XMLのノードを分解し、属性(attribute) やら要素(element)を取り出す。所謂パース(構文解析)だ。
 しかしここで問題発生。
 何と、改行やタブを含んだxmlファイルが正しくパースできない。
 なんでよ。

 で、Webの検索をしたりiChatで訪ねたりと、解決法を探ってみたら、なんと改行・タブを全部ブランクのノードとしてしまうらしい、ってどーゆー仕様やねん。
 FlashのXMLオブジェクトには、その空白を無視する属性があって、それがignoreWhiteだ。こいつをtrueに設定すると良い。何故か、これが最初はfalseになっている。
 XMLはそもそも、文書を見やすくするため空白や改行を任意の量入れて良いことになっている。だから、無視するのが当たり前の作法なのに、何故にデフォルトがfalse???
 理解できん。
 ともかく、最初のフレームで次のようにしておけば、その後は空白を無視する。

XML.prototype.ignoreWhite = true;

要素を取り出す

 で、いよいよパースしていくわけだ。
 XMLオブジェクトの属性を使って、取り出していくわけだ。
 まず、xmlファイルには、必ず根元(ルート)となるノードがある。
 だから、とりあえず、次のようなスクリプトで根元を取り出す必要がある。

rootNode = theXML.firstChild;

 取り出したrootNodeもXMLオブジェクトとなる。
 んで、最初のノードを取り出すのはfirstChildだけど、ノードが一つだけってことは、まずルート以外考えられない。
 従って、次に取り出すのは、ノードの集団と言うことになる。
 次のようにchildNodesを利用することで、ノードの集団、つまり配列が手に入る。

elementNodes = rootNode.childNodes;

 あとは、for〜inループを使って、要素を取り出していけば良い。
 だいたい次のような感じ。

for(curNum in elementNodes){
	trace(elementNodes[curNum]);
}

 さらに階層化されている場合は、childNodesを使って階層をたどっていけば良いわけだ。
 各要素の名前と、値を取るのは次のようになる。名前は良いとして、値の方は感覚よりも一つ奥の階層を指定する必要がある。この際firstChild.nodeValueと、セットで使うと覚えた方がよさそう。

for(curNum in elementNodes){
	trace("要素名:"elementNodes[curNum].nodeName+"---値:"+elementNodes[curNum].firstChild.nodeValue);
}

属性を取り出す

 要素ではchildNodesを使ったが、属性を取り出したい場合はattributesを使う。
 属性は、要素と違い1ノードに同じものが2つ以上あったりしないので、名前でアクセスできる。

curNode.attributes["name"]

 もちろん、要素と同じように、for〜inを使って順に取り出すこともできる。
 ここで、変数attributeNameに入るのは、数字ではなく名前。つまり、attributesの返り値は、配列ではなく連想配列であると言うことである。

for(attributeName in rootNode.attributes){
	trace(rootNode.attributes[attributeName]);
}

要素を配列+連想配列に代入

 XMLでわりと多い(と予想できる)のは、カード形データベース風な構造になっているパターン。
 住所録であれば、次のような感じの構造では無かろうか。

ルート
├カード
│├名前
│└住所
├カード
│├名前
│└住所
:

 データが必要になる度にXMLをパースしていたのでは面倒だし。それぞれのスクリプトで使いやすいデータ構造を持ったものに変換しておくのが良いだろう、XMLそのままの方が運用しやすい場合もあるだろうが。
 例えば、カードの部分は配列に、名前と住所は連想配列にする。もしくは、名前が一意(unique)と考えられるならば、カードの部分を名前による連想配列としたい。
 ここでは、カードが配列、名前と住所は連想配列と言う構造のデータにする。

function XML2List() {
	var elementNodes = theXML.firstChild.childNodes;
	var cardList = new Array();
	//XMLから連想配列(ハッシュ)に変換
	for (cardNum in elementNodes) {
		//配列の中に、連想配列(Objectを利用)を配置
		cardList[cardNum] = new Object();
		//データ部分のノードの取り出し
		var dataNodes = elementNodes[cardNum].childNodes;
		//連想配列に、各データを代入する
		for (dataNum in dataNodes) {
			var curData = dataNodes[dataNum];
			cardList[cardNum][curData.nodeName] = curData.firstChild.nodeValue;
		}
	}
	//結果を出力
	for (cardNum in cardList) {
		trace("----------card:" + cardNum);
		for (dataName in cardList[cardNum]) {
			trace(dataName + ":" + cardList[cardNum][dataName]);
		}
	}
	//実際運用するときは、出来上がったcardListを返すという関数になるだろう
	//return cardList;
}

 とまぁ、こんな感じの関数となった。関数の前提条件として、インスタンス変数のtheXMLに読み込んだxmlファイルが入っている必要がある。

 なーんか、えらく手間どったけど、とにかくXMLを読み込むことができた。

 今日はここまで。


2003-07-12