AS3对象序列化存储
23.6.1.问题
我想对自定义对象进行序列化并保存到硬盘驱动器的文件中。
23.6.2.解决办法
通过class-alias注册自定义类,使用ActionScript Message Format (AMF) 编码序列化对象,并使用FileStream.writeObject方法把对象存储到文件上。
23.6.3.讨论
使用AIR文件系统API,你可以把经过AMF编码序列化过的对象写入文件流缓冲区中。
ActionScript语言中大多数内建对象类型比如String和Boolean都自动支持序列化。这些类型同通过AMF被编码为二进制格式,当经过反序列化可以恢复原来的值。然而自定义对象是不自动支持序列化的,要启动自定义对象序列化支持,你需要用registerClassAlias 方法用一个类别名注册此类,或者在类定义前申明这个[RemoteClass]元数据标签。
假定你的程序要把用户信息作为一个对象进行显示,可过一段时间被同一个程序或者被其他程序载入,这些程序应已知道如何处理该对象,要表现用户信息的自定义对象看起来大概是下面这样:
+展开
-ActionScript
package com.oreilly.flexcookbook {
[RemoteClass]
[Bindable]
public
class UserData {
public
var firstName:String;
public
var lastName:String;
public
var age:Number;
public
var sessionLength:Number;
public
function UserData( firstName:String =
"",lastName:String =
""){
this.firstName = firstName;
this.lastName = lastName;
}
}
}
每次你的程序被用户打开时,一个新的UserData对象被创建,等待用户输入信息,通常可以保存这些信息以特定的格式保存到文本文件上,但是你也可以序列化对象到文件上,从而可轻易的以对象类型读取,不必要解析字符串,重置UserData新实例属性。要序列化对象且通过反序列化保留其属性值,需要使用[RemoteClass]元数据标签注册它。
在类申明前插入[RemoteClass] 元数据标签,可启动此类在使用AMF序列化和反序列化中能保持类型信息和属性值。你也可以在变量定义前加上[Transient]元数据标签,指示此变量不会被序列化。比如上面例子中的sessionLength属性每次程序运行时都会更新,因为没必要进行保存,你可以把此属性标记为transient属性拒绝被序列化:
+展开
-ActionScript
package com.oreilly.flexcookbook {
[RemoteClass]
public
class UserData {
public
var firstName:String;
public
var lastName:String;
public
var age:Number;
[Transient]
public
var sessionLength:Number;
public
function UserData( firstName:String =
"",lastName:String =
""){
this.firstName = firstName;
this.lastName = lastName;
}
}
}
使用FileStream.writeObject方法把序列化自定义对象写到文件里,当使用writeObject和readObject 写入和读取二进制数据默认所用的编码格式是AMF 3 规范。通过设置FileStream.objectEncoding属性,你可以使用AMF 0 格式,下面的例子保存用户信息到扩展名为.user的文件上:
+展开
-XML
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
import com.oreilly.flexcookbook.UserData;
private
var userData:UserData =
new UserData();
private
static
const EXT:String =
".user" ;
private
function submitHandler():
void {
userData.firstName = firstField.text;
userData.lastName = lastField.text;
userData.age = ageField.value;
saveUserData();
}
private
function saveUserData():
void
{
var fnm:String = userData.firstName +
"_"+userData.lastName + EXT;
var file:File =File.desktopDirectory.resolvePath( fnm );
var stream:FileStream =
new FileStream();
stream.open( file, FileMode.WRITE );
stream.writeObject( userData );
stream.close();
}
]]>
</mx:Script>
<mx:Form>
<mx:FormItem label="First Name:">
<mx:TextInput id="firstField"
change="{submitBtn.enabled = firstField.text != ''}"/>
</mx:FormItem>
<mx:FormItem label="Last Name:">
<mx:TextInput id="lastField"
change="{submitBtn.enabled = lastField.text != ''}"/>
</mx:FormItem>
<mx:FormItem label="Age:">
<mx:NumericStepper id="ageField" minimum="18" maximum="110" />
</mx:FormItem>
<mx:Button id="submitBtn" label="submit" enabled="false" click="submitHandler();" />
</mx:Form>
</mx:WindowedApplication>
当用户输入并提交信息后,submitHandler事件处理器被调用,UserData对象被更新,接着调用saveUserData方法。在saveUserData方法中,用UserData实例的firstName和lastName属性组成文件路径,文件被保存到桌面上,扩展名为.user。当反序列化时,文件被再次打开,对象属性值将被会恢复。下面的例子扩展了之前的程序,提供对打开.user文件的支持:
+展开
-XML
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Script>
<![CDATA[
import com.oreilly.flexcookbook.UserData;
[Bindable]
private
var userData:UserData =
new UserData();
private
var file:File = File.desktopDirectory;
private
var filter:FileFilter =
new FileFilter(
"User File",
"*.user");
private
static constEXT:String =
".user";
private
function submitHandler():
void{
userData.firstName = firstField.text;
userData.lastName = lastField.text;
userData.age = ageField.value;
saveUserData();
}
private
function saveUserData():
void {
var fnm:String = userData.firstName +
"_" + userData.lastName + EXT;
var file:File = File.desktopDirectory.resolvePath( fnm );
var stream:FileStream =
new FileStream();
stream.open( file, FileMode.WRITE );
stream.writeObject( userData );
stream.close();
}
private
function openHandler():
void {
file.browseForOpen(
"Open User", [filter] );
file.addEventListener( Event.SELECT, selectHandler );
}
private
function selectHandler( evt:Event ):
void {
var stream:FileStream =
new FileStream();
stream.open( file, FileMode.READ );
userData = stream.readObject();
}
]]>
</mx:Script>
<mx:Form>
<mx:FormItem label="First Name:">
<mx:TextInput id="firstField"
text="{userData.firstName}"
change="{submitBtn.enabled = firstField.text != ''}"/>
</mx:FormItem>
<mx:FormItem label="Last Name:">
<mx:TextInput id="lastField"
text="{userData.lastName}"
change="{submitBtn.enabled = lastField.text != ''}"/>
</mx:FormItem>
<mx:FormItem label="Age:">
<mx:NumericStepper id="ageField" value="{userData.age}" minimum="18" maximum="110" />
</mx:FormItem>
<mx:Button id="submitBtn" label="submit" enabled="false" click="submitHandler();"
/>
</mx:Form>
<mx:HRule width="100%" />
<mx:Button label="open" click="openHandler();" />
</mx:WindowedApplication>
当用户点击open按钮时,File.browseForOpen方法会打开一个对话框,并设置了过滤器只显示.user 扩展名的文件。当一个文件被选中后, selectHandler 方法被调用, 通过FileStream.readObject方法将文件读入到缓冲区,readObject方法将反序列化UserData对象,通过数据绑定将保存的数据显示到表单。
FileStream.writeObject和FileStream.readObject方法提供了很多方法用于序列化数据对象到文件系统上。通过[RemoteClass]元数据标签注册类即可用AMF将类进行编码为二进制格式。
如果你是从文本文件中处理载入和解析键--值对自定义对象,那么序列化自定义对象将大大减少开发开销