読者です 読者をやめる 読者になる 読者になる

謎言語使いの徒然

適当に気になった技術や言語を流すブログ。

XML読み込んでみる。

Flex OSS Tips

最初に XML を拾って解析してみる。

tags.xml

<?xml version="1.0" encoding="utf-8" ?>
<tags>
	<tag id="0" name="猫" />
	<tag id="1" name="犬" />
	<tag id="2" name="動物" />
</tags>

contents.xml

<?xml version="1.0" encoding="utf-8" ?>
<contents>
	<item title="窓辺の淑女" url="image/sample.jpg" thumb="thumb/sample.jpg">
		<tag id="0" />
		<tag id="2" />
		<comment>
			<![CDATA[
			窓辺にたたずむ優雅な猫。
			]]>
		</comment>
	</item>
</contents>

モデルデータの実態はこんなフォーマットで良いかな。
XML を読み込む手順が以下。

public class XMLConfigLoader extends EventDispatcher
{
	private var request:URLRequest;
	private var loader:URLLoader;
	private var xmlData:XML;
	
	public function XMLConfigLoader(targetUrl:String) 
	{
		this.request = new URLRequest(targetUrl);
		
		this.loader = new URLLoader(this.request);
		this.loader.addEventListener(Event.COMPLETE, this.loadongEvent);
		this.loader.addEventListener(IOErrorEvent.IO_ERROR, this.loadongEvent)
		this.loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.loadongEvent);
	}
	
	public function load():void
	{
		this.loader.load(this.request);
	}
	
	private function loadongEvent(event:Event):void
	{
		switch(event.type)
		{
			case Event.COMPLETE:
				this.xmlData = new XML(event.target.data);
				dispatchEvent(event);
				break;
			case IOErrorEvent.IO_ERROR:
			case SecurityErrorEvent.SECURITY_ERROR:
				dispatchEvent(new Event(Event.CANCEL));
				break;
		}
	}
	
	public function get xml():XML
	{
		return this.xmlData;
	}
}

XML の読み込み処理があまりにも定型句なので、こんなクラスを作って簡略化。
しかし、、、、tags は別に Dictionary で key = value にしとけばいいとして、問題は contents だ。
こんなの実行時に生データで弄ってらんない。

public class ContentItem
{
	private var _title:String;
	private var _url:String;
	private var _thumb:String;
	private var _tag:Array;
	private var _comment:String;
	
	public function ContentItem(xml:XML, tags:Object) 
	{
		this._title = xml.@title;
		this._url = xml.@url;
		this._thumb = xml.@thumb;
		this._comment = xml.@comment;
		this._tag = [];
		
		var tagList:XMLList = xml.tag;
		for each(var target:XML in tagList)
		{
			var id:int = target.@id;
			this._tag.push(tags[id]);
		}
	}
	/* get/set は略 */
}

こんなんを書いておく。
そして、設定ファイルローダを作る。

public class ConfigLoader extends EventDispatcher
{
	private var tagLoader:XMLConfigLoader;
	private var contentLoader:XMLConfigLoader;
	
	public function ConfigLoader() 
	{
		// ファイルのロード準備
		this.tagLoader = new XMLConfigLoader("tags.xml");
		this.contentLoader = new XMLConfigLoader("contents.xml");
	}
	
	public function load():void
	{
		// 両方ロードが完了した時点でイベント通知。
		var completeCount:int = 0;
		var callCount:int = 0;
		
		// 各ファイルのロード完了、もしくは失敗時の処理
		var result:Function = function(eve:Event):void {
			
			callCount++;
			switch(eve.type)
			{
				case Event.COMPLETE:
					completeCount++;
					break;
				case Event.CANCEL:
					break;
			}
			
			// 全ファイル正常読み込み完了
			if (callCount == 2 && completeCount == 2)
				dispatchEvent(eve);
			
			// 1 個以上読み込み失敗
			if (callCount == 2 && completeCount != 2)
				dispatchEvent(new Event(Event.CANCEL));
			
			// 読み込み結果取得完了時に、イベントリスナ開放。
			if (callCount == 2)
			{
				tagLoader.removeEventListener(Event.COMPLETE, arguments.callee);
				contentLoader.removeEventListener(Event.COMPLETE, arguments.callee);
				
				tagLoader.removeEventListener(Event.CANCEL, arguments.callee);
				contentLoader.removeEventListener(Event.CANCEL, arguments.callee);
			}
		};
		
		// イベントリスナを仕掛ける。
		tagLoader.addEventListener(Event.COMPLETE, result);
		contentLoader.addEventListener(Event.COMPLETE, result);
		
		tagLoader.addEventListener(Event.CANCEL, result);
		contentLoader.addEventListener(Event.CANCEL, result);
		
		// ローディング開始
		this.tagLoader.load();
		this.contentLoader.load();
	}
	
	private var _tags:Object = null;
	
	public function get tags():Object
	{
		// 初回取得時に xml 解析
		if (this._tags == null)
		{
			var tagXML:XML = this.tagLoader.xml;
			var lists:XMLList = tagXML.tag;
			this._tags = {};
			
			for each(var target:XML in lists)
			{
				var id:String = target.@id;
				var name:String = target.@name;
				
				this._tags[id] = name;
			}
		}
		
		return this._tags;
	}
	
	private var contents:Object = null;
	
	public function getContentsByTag(tagId:String):Array
	{
		// 初回取得時に xml 解析
		if (this.contents == null)
		{
			this.contents = { };
			var tagXML:XML = this.contentLoader.xml;
			var lists:XMLList = tagXML.item;
			
			// タグごとに配列を準備
			var taglist:Object = this.tags;
			for (var index:String in taglist)
				this.contents[taglist[index]] = [];
			this.contents["allContents"] = [];
			
			// アイテム解析、登録
			for each(var target:XML in lists)
			{
				var item:ContentItem = new ContentItem(target, taglist);
				var instag:Array = item.tag;
				
				this.contents["allContents"].push(item);
				for each(var ins:String in instag)
					this.contents[ins].push(item);
			}
		}
		
		return this.contents[tagId];
	}
}

設定ファイルのサイズが大きくなって、タグごとにファイルが分離する場合も、この設定ローダで吸収しておけばよさげ。
そして、実際に走らした。

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
			   xmlns:s="library://ns.adobe.com/flex/spark"
			   xmlns:mx="library://ns.adobe.com/flex/mx"
			   width="1024" height="700" backgroundColor="#442222"
			   creationComplete="initializer()">
	
	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<fx:Script>
		<![CDATA[
import flash.events.Event;
import mx.containers.Canvas;
import net.azaleaworks.utils.XMLConfigLoader;
import photo.ConfigLoader;
import flash.utils.describeType;
import photo.ContentItem;
		
		private var tags:ConfigLoader;

		private function initializer():void
		{
			tags = new ConfigLoader();
			tags.addEventListener(Event.COMPLETE, this.completeHandler);
			tags.load();
		}
		
		private function completeHandler(event:Event):void
		{
			// チェック
			var contents:Array = this.tags.getContentsByTag("allContents");
			var item:ContentItem = contents[0];
			trace(item.title);
		}
		]]>
	</fx:Script>
	<mx:TabNavigator width="100%" height="100%" id="tabNav">
		<mx:Canvas width="100%" height="100%" label="全般" id="allContents" />
	</mx:TabNavigator>
</s:Application>

FlashDevelop でブレークできないの初めて知った、、、、。
プラグインが出てるけど、最新版のFlashDevelopとは相性が悪いようだ。*1
http://orange.zero.jp/zbn39616.pine/download/download.html

*1:推奨 3.0 で FlashDevelop 最新が 3.1.1