代码改变世界

ActionScript 3 中的对象序列化

2008-11-12 09:06  宝宝合凤凰  阅读(1436)  评论(0编辑  收藏  举报
我们都知道对象序列化的目的是为了对象能够在网络上传输,以便存储对象,这是很有用处的,比如一个在线Flash游戏,玩家玩到一半想明天再玩,这时需要通过对象序列化把游戏数据保存起来,不然关闭浏览器数据就都没了,又要从头开始,当然利用Flash中的LSO也可以进行存储数据,不过是存在客户端上,这样的数据不一定能持久,对象序列化的主要目的是保存到服务器的数据库里,也许有人会问,干嘛不把对象的属性直接保存到数据库,还这么麻烦饶一大圈,这当然可以,但是如果对象的属性非常多,写起来会不会很繁琐呢,不如直接把对象保存起来这样反而跟简便,对象序列化的原理是把对象转换为字符串的形式,因为字符串任何语言都认识都能处理,而对象类型只能是属于特定的语言,比如一个Rectangle实例有width,height,fillColor,lineColor属性,把它转换为字符串格式后:"width=value|height=value|fillColor=value|lineColor=value"。
    ActionScript 3本身并没有提供对象序列化的APIs,因为它已经有了flash remoting(AMF协议),AMF协议完成对象的序列化和反序列化,这里我们先不谈AMF,看看如何自定义实现对象的序列化,OReilly.Essential.ActionScript.3.0的作者提供了一系列类和接口来实现对象序列化,我们看看他是如何做的:

定义了一个Serializable接口,只声明一个方法用于对象的序列化:
package serializer
{
    
public interface Serializable
    
{
        function serialize():String;
    }

}

定义一个实现Serializable接口的Serializer类,用于完成对象
package serializer
{
    
public class Serializer implements Serializable
    
{
        
private var serializationVars:Array; //存储对象的属性
        
private var serializationObj:Serializable; //指向可序列化的对象引用
        
private var recordSeparator:String; //指定属性之间的分隔符
        
        
public function Serializer(){
            setSerializationObj(
this);
        }

        
public function setSerializationVars(vars:Array):void{
            serializationVars 
= vars;
        }

        
public function setSerializationObj(obj:Serializable):void{
            serializationObj 
= obj;
        }

        
public function setRecordSeparator(rs:String):void{
            recordSeparator 
= rs;
        }

        
public function serialize():String
        
{
            var s:String 
= "";
            
for (var i:int = serializationVars.length; -->= 0;){
                s 
+= serializationVars[i] + "=" + String(serializationObj[serializationVars[i]]);
                
if(i>0){
                    s 
+= recordSeparator;
                }

            }

            
return s;
        }

        
    }

}

一个继承Serializer的Point类,我们将对Point类实例进行序列化处理:
package serializer
{
    
public class Point extends Serializer
    
{
        
public var x:Number;
        
public var y:Number;
        
public var name:String = "Point";
        
        
public function Point(x:Number,y:Number)
        
{
            
super();
            setRecordSeparator(
","); //设置分隔符
            setSerializationVars([
"x","y"]); //序列化x,y属性
            
this.x = x;
            
this.y = y;
        }

        
    }

}

测试类,输出结果为:y=6,x=5
package
{
    
import flash.display.Sprite;
    
import serializer.Point;
    
import serializer.Rectangle;
    
import serializer.Rectangle;

    
public class TestSerialization extends Sprite
    
{
        
public function TestSerialization()
        
{
            
super();
            var p:Point 
= new Point(5,6);
            trace(p.serialize());     

        }

        
    }

}

我们看到已经成功把Point对象变成字符串“y=6,x=5”,我们再来看一个例子:

Shape类,定义了两个属性fillColor,lineColor:
package serializer
{
    
public class Shape
    
{
        
public var fillColor:uint = 0xFFFFFF;
        
public var lineColor:uint = 0;
        
        
public function Shape(fillColor:uint,lineColor:uint){
            
this.fillColor = fillColor;
            
this.lineColor = lineColor;
        }

    }

}

Rectangle类继承Shape,同时实现Serializable接口:
package serializer
{
    
public class Rectangle extends Shape implements Serializable
    
{
        
public var width:Number = 0;
        
public var height:Number = 0;
        
public function Rectangle(fillColor:uint, lineColor:uint)
        
{
            
super(fillColor, lineColor);
        }

        
public function setSize(w:Number,h:Number):void{
            width 
= w;
            height 
= h;
        }

        
public function getArea():Number{
            
return width*height;
        }

        
public function serialize():String
        
{
            var ser:Serializer 
= new Serializer();
            ser.setRecordSeparator(
"|");
            ser.setSerializationVars([
"height","width","fillColor","lineColor"]);
            ser.setSerializationObj(
this);
            
return ser.serialize();
        }

        
    }

}
 
一个类要想能够被序列化,要么实现Serializable接口,要么继承已经实现Serializable接口的类,因为Rectangle类的父类没有实现Serializable接口,所以Rectangle类必须实现Serializable接口,而上面那个Point类的父类已经实现Serializable接口,等于是间接实现了,我们看到Rectangle类的serialize()方法,把Serializer当作序列化工具,对Rectangle对象进行序列化处理,  对于核心类Serializer作者设计的比较巧妙,能同时处理两种情况的序列化。
    最后还是要说一下一个小问题,假如序列化后的字符串保存到数据库了,再读取进行反序列化时,该怎么知道这些字符串是什么类的呢??? 我的想法是在序列化时同时保存类名以便为反序列化做准备,拿Point例子来说:
增加一个属性代表类名:
package serializer
{
    
public class Point extends Serializer
    
{
        
public var x:Number;
        
public var y:Number;
        
public var name:String = "Point";
        
        
public function Point(x:Number,y:Number)
        
{
            
super();
            setRecordSeparator(
",");
            setSerializationVars([
"x","y","name"]);
            
this.x = x;
            
this.y = y;
        }

        
    }

}

 

 

registeregisterClassAlias的用法

registerClassAlias在利用AMF3进行序列话网络通讯中,是非常有用的。需要把客户端的对象直接传送到服务器得时候,保留该对象的类(类型)。 这样的话,就可以传送自定义对象或者系统自带对象。
具体的使用方法,官方有详细地用法。这里我只说说自己的一些理解。
readObject方法对构造器有参数的类,是会出错的,会弹出参数数量不匹配这个错误。因为还原对象进行反射的时候,是默认没有参数的给构造器的。这也是有些人 讨论Sprite等对象不能进行深度拷贝的原因
所以在使用AMF3进行序列话的时候要注意这个了,还有一个就是,如果那个类包含了多个类,也就是个复合类,那么里面的那个复合类,也必须进行registerClassAlias,例如

  1. package
  2. {
  3.     import flash.geom.Point;
  4.     
  5.     public class Test
  6.     {
  7.         public var name:String;
  8.         public var point:Point;
  9.     }
  10. }
  11. 这样需要写两条语句才能完全把Test序列化
  12. registerClassAlias("point",Point);
  13. registerClassAlias("test",Test)

具体使用例子,参考官方例子,下面是摘录出来的


此示例使用 registerClassAlias() 函数为 ExampleClass 注册一个别名  ( com.example.eg )。 由于为类注册了别名,因此可以将对象作为 ExampleClass 的实例反序列化,且代码将输出  true。 如果删除 registerClassAlias() 调用,则代码将输出 false

  1. package {
  2.     import flash.display.Sprite;
  3.     import flash.net.registerClassAlias;
  4.     import flash.utils.ByteArray;
  5.     public class RegisterClassAliasExample extends Sprite {
  6.         public function RegisterClassAliasExample() {
  7.             registerClassAlias("com.example.eg", ExampleClass);
  8.             var eg1:ExampleClass = new ExampleClass();
  9.             var ba:ByteArray = new ByteArray();
  10.             ba.writeObject(eg1);
  11.             ba.position = 0;
  12.             var eg2:* = ba.readObject();
  13.             trace(eg2 is ExampleClass); // true
  14.         }
  15.     }
  16. }