AS3とGAEで連想配列をセーブ&ロード

概要

AS3の連想配列を、GAEで保存し、そのデータをAS3からロードするところまで。
まるで慣れていないので、もう少しましな方法があるはず。

解説

基本は前回と同じくこちらを参考にしつつ、Twitterもどきを作りながらGoogle App Engineの肝データストアを理解する - builder by ZDNet Japanからセーブ&ロードを組み込む。

var data:Object = {foo:"bar"};

という「foo→bar」の連想配列をセーブ&ロードする。


まず、AS3側のコードは以下の通り。

package{
	import flash.display.Sprite;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.net.NetConnection;
	import flash.net.Responder;

	public class test extends Sprite {

		private var netConnection:NetConnection;
		private var responder_save:Responder;
		private var responder_load:Responder;
		private var output:TextField;

		public function test() {
			output = new TextField();
			output.autoSize = TextFieldAutoSize.LEFT;
			addChild(output);

			netConnection = new NetConnection();
			netConnection.connect("http://localhost:8080/");

			responder_save = new Responder(saveOnComplete, saveOnFail);
			responder_load = new Responder(loadOnComplete, loadOnFail);

			//save test
			var data:Object = {foo:"bar"};
			save(data);

			//load test
			load();
		}

		//=Save=

		//セーブ本体
		private function save(data:Object):void{
			//Python側の save を呼び出す。
			netConnection.call("save", responder_save, data);//save(data)     
		}

		//セーブ成功時
		private function saveOnComplete():void {
		}

		//セーブ失敗時
		private function saveOnFail(results:*):void {
			//原因を表示
			for each (var thisResult:String in results){
				output.appendText(thisResult);
			}
		}

		//=Load=

		//ロード本体
		private function load():void{
			//Python側の load を呼び出す。
			netConnection.call("load", responder_load);//load()
		}

		//ロード成功時
		private function loadOnComplete(data:Object):void {
			//連想配列(Object)が返ってくるので、keyをfooとした時のvalを表示
			var val:String = "Loaded:" + data.foo;//[foo->bar]
			output.htmlText = val;
		}

		//ロード失敗時
		private function loadOnFail(results:*):void {
			//原因を表示
			for each (var thisResult:String in results){
				output.appendText(thisResult);
			}
		}
	}
}


GAEへ値を渡しているのは、関数saveの

netConnection.call("save", responder_save, data);//save(data)     

の部分。これによって、GAE側の「save」という関数(おそらく厳密には'save'に対応付けられた関数)を呼び、引数としてdata(=連想配列)を渡し、その結果をresponder_saveが受け取る。


GAEから値を受け取っているのは、関数loadの

netConnection.call("load", responder_load);//load()

の部分で、GAE側の「load」という関数を呼び、その結果をresponder_loadが受け取る。ロードが成功した場合、GAEに渡しておいた連想配列が、responder_loadに登録しておいたloadOnCompleteに引数として渡される。この例では、連想配列が「foo→bar」であることを確かめるため、fooをキーとしてその返り値を表示している。




対して、GAE側のコードは以下の通り。

import os
import wsgiref.handlers
from pyamf.remoting.gateway.wsgi import WSGIGateway
from pyamf import ASObject

from google.appengine.api import users
from google.appengine.ext import db,webapp
from google.appengine.ext.webapp import template


#格納するデータ(db.Modelを継承する必要がある)
class Map(db.Model):
	m_key = db.StringProperty()
	m_val = db.StringProperty()

#Save
def save(data):
	#dataを連想配列として、全てのデータにアクセス
	for k, v in data.iteritems():
		#格納するデータを作成
		map = Map(m_key=k, m_val=v)
		#保存
		map.put()

#Load
def load():
	#全てのデータを取ってくるクエリを作成
	q = Map.all()
	#個数を1個に制限してみる(今回は意味がないと思うが)
	result = q.fetch(limit=1)
	for map in result:
		#一番最初のMapを連想配列にして返す
		return {map.m_key:map.m_val}
	#何もなければエラー用連想配列を返す
	return {'err':'err'}

#対応付け
services = {
	'save': save,
	'load': load,
}

#Main
def main():
	application = WSGIGateway(services)
	wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
	main()


GAEでセーブするには、db.Modelを継承したデータでないとダメらしいので、連想配列(というかペア)の役割を果たすMapというデータを作り、それで保存することにする。


AS3から受け取ったデータをセーブするのは関数「save」で、dataが連想配列として入ってくるので、それをMapに変換してputによってセーブしている。


データのロードは関数「load」で、クエリを作成してデータを持ってくる。クエリの書き方は色々あるらしいが、ここでは全てのデータを持ってくる「Map.all()」でクエリを作成。(テストとしてさらにfetchを使っているが、おそらくこれはなくていい。)それをもとに、各データを処理するループを作成し、一番最初のデータをMapから連想配列に変換して返すようにしている。


確認の方法は前回と同じく、

  • test.asから「mxmlc test.as」でtest.swfを作成
  • python ..\..\dev_appserver.py .」というバッチファイルを作っておいて実行
  • http://localhost:8080/test.swfにアクセスして、「Loaded:bar」と表示されれば成功

という流れ。

  • 注意点
    • このswfを実行するたびにデータが蓄積されるので、テストが終了したら「http://localhost:8080/_ah/admin/」で「Map」と入力して出て来るものを削除した方が良い
    • GAE(Python)側で連想配列として扱っているものは、もしかしたらASObjectかもしれない(未確認。でも連想配列として扱うことは可能)。

予定

セーブ&ロードのテストは終了したので、オワタの方のステージデータをセーブできるようにしてみる。その後、時間が合ったらステージ作成のGUIの方に取り掛かる。