导航

.Net中的序列化基础

Posted on 2009-11-06 15:03  阳光有约  阅读(144)  评论(0编辑  收藏  举报

 http://www.cnblogs.com/ibmfm/archive/2006/01/13/316359.html

 

.Net中的序列化方法有三种:XML 序列化、SOAP 序列化和二进制序列化。若是序列化到文件的话,前两者生成的是 XML 文件,二进制序列化生成二进制文件。


    跟序列化相关的两个类型:
    SerializableAttribute:指示一个类是可以序列化的。
    ISerializable:使对象可以自己控制其序列化和反序列化的过程。

    列表比较三种序列化方法。


XML SOAP 二进制
序列化器类 XmlSerializer SoapFormatter BinaryFormatter
SerializableAttribute  标记 不需要 需要
ISerializable 接口 不需要实现,实现了也不起作用。 可以不实现,但实现了就起作用。
无参构造函数 必须有,系统提供的缺省无参构造函数也算。 不需要,因为反序列化时不调用构造函数。
被序列化的数据成员 公共属性和字段 所有
产生文件大小

    XML序列化的优点是使用简单,也颇具灵活性,比如可以控制数据在 XML 文件中是作为 Element 还是作为 Attribute ,以及显示的名称等。XML 序列化对一般的应用是足以应付的,但当序列化循环引用的对象,即有多个引用指向同一个对象实体时,XML 序列化机制将在每个引用的地方都创建一个对象副本。这除了会导致数据存储上的冗余外,更严重的是使一个对象在反序列化后变成了毫无关系的多个对象,即 XML 反序列化后可能得到错误的对象关系图表。比如下图所示的简单例子:


    对应三种类型分别有三个对象 cObject 及其成员 _aObject、 _bObject,_aObject 由 cObject 构造,_bObject 中存的是对 _aObject 的引用,即 cObject 的成员 _aObject 和 _bObject 的成员 _aObject 是同一个东西。则 ClassC 类型的对象 cObject 经 XML 序列化的结果是:
<ClassC xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
  
<Name>ccc</Name> 
  
<AObject> 
    
<ID>37</ID> 
    
<Name>aaa</Name> 
  
</AObject> 
  
<BObject> 
    
<Name>bbb</Name> 
    
<AObject> 
      
<ID>37</ID> 
      
<Name>aaa</Name> 
    
</AObject> 
  
</BObject> 
</ClassC> 
     
    从这个结果反序列化后得到的新的 cObject,其成员 _aObject 跟 _bObject 中的 _aObject 可就是两个对象了。要解决这个问题,我能想到的就是给对象加上 GUID 属性,在反序列化后根据 GUID 属性重新设置引用,不知还有没有其它办法。
   
    SOAP 和二进制序列化的优点是可以精确地控制序列化及反序列化的过程,并可以序列化对象的非公共成员。所以对复杂对象的序列化,我们应该在实现 ISerializable 接口后,用 SOAP 或 二进制的方式保存数据。至于缺点,如果你嫌在类名上加个 Serializable 标记很麻烦的话,这也许算个缺点。
    还是上面的例子,如果用 SOAP 序列化 cObject 对象,结果是: 
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> 
    
<SOAP-ENV:Body> 
        
<a1:ClassC id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/SerializeTest.CrossReference/SerializeTest%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull"> 
            
<ClassCNameName id="ref-3">ccc</ClassCNameName> 
            
<ClassCAObjectAObject href="#ref-4"/> 
            
<ClassCBObjectBObject href="#ref-5"/> 
        
</a1:ClassC> 
        
<a1:ClassA id="ref-4" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/SerializeTest.CrossReference/SerializeTest%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull"> 
            
<ClassANameName id="ref-6">aaa</ClassANameName> 
            
<ClassAIDID>37</ClassAIDID> 
        
</a1:ClassA> 
        
<a1:ClassB id="ref-5" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/SerializeTest.CrossReference/SerializeTest%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull"> 
            
<ClassBNameName id="ref-7">bbb</ClassBNameName> 
            
<ClassBAObjectAObject href="#ref-4"/> 
        
</a1:ClassB> 
    
</SOAP-ENV:Body> 
</SOAP-ENV:Envelope> 

    很明显,里面存的是对象引用,这是一个精确副本,反序列化后毫无问题。 

 附:ClassC 的一段代码:
        public ClassC()
        
{
            _name = "Unknown ClassC Object";
            InitData();
        }


        
private void InitData()
        
{
            _aObject = new ClassA( 1 );
            _aObject.Name = "aaa";
            _aObject.ID = 37;

            _bObject = new ClassB();
            _bObject.Name = "bbb";
            _bObject.AObject = _aObject;
        }