zoukankan      html  css  js  c++  java
  • .net序列化及反序列化

    什么是序列化?
      ---.net的运行时环境用来支持用户定义类型的流化的机制。它是将对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。
      
      序列化的目的:
      1、以某种存储形式使自定义对象持久化;
      2、将对象从一个地方传递到另一个地方。
      
      实质上序列化机制是将类的值转化为一个一般的(即连续的)字节流,然后就可以将该流写到磁盘文件或任何其他流化目标上。而要想实际的写出这个流,就要使用那些实现了IFormatter接口的类里的Serialize和Deserialize方法。

    1。 XML Serializer。这个是 ASP。NET 中 Web Service SOAP 请求的发送和接受默认使用的方式。指序列化对象的公共属性和成员。

    2。 SOAP Serializer . DotNet Remoting 使用的对象传送方式。这个时候传送的对象要求有 Serializable 标志。

    3.    BinarySerializer 。同2, 只不过是二进制格式。

    在.net框架里提供了这样两个类:
      
      一、BinaryFormatter
      
      BinaryFormatter使用二进制格式化程序进行序列化。您只需创建一个要使用的流和格式化程序的实例,然后调用格式化程序的 Serialize 方法。流和要序列化的对象实例作为参数提供给此调用。类中的所有成员变量(甚至标记为 private 的变量)都将被序列化。
      
      首先我们创建一个类:
      [Serializable]
      public class MyObject {
       public int n1 = 0;
       public int n2 = 0;
       public String str = null;
      }
      Serializable属性用来明确表示该类可以被序列化。同样的,我们可以用NonSerializable属性用来明确表示类不能被序列化。
      接着我们创建一个该类的实例,然后序列化,并存到文件里持久:
      MyObject obj = new MyObject();
      obj.n1 = 1;
      obj.n2 = 24;
      obj.str = "一些字符串";
      IFormatter formatter = new BinaryFormatter();
      Stream stream = new FileStream("MyFile.bin", FileMode.Create,
      FileAccess.Write, FileShare.None);
      formatter.Serialize(stream, obj);
      stream.Close();
      
      而将对象还原到它以前的状态也非常容易。首先,创建格式化程序和流以进行读取,然后让格式化程序对对象进行反序列化。
      IFormatter formatter = new BinaryFormatter();
      Stream stream = new FileStream("MyFile.bin", FileMode.Open,
      FileAccess.Read, FileShare.Read);
      MyObject obj = (MyObject) formatter.Deserialize(fromStream);
      stream.Close();
      
      // 下面是证明
      Console.WriteLine("n1: {0}", obj.n1);
      Console.WriteLine("n2: {0}", obj.n2);
      Console.WriteLine("str: {0}", obj.str);
    二 XmlSerializer  使用二进制格式化程序进行序列化。
        using System.Xml.Serialization;

      序列化

     XmlSerializer xml = new XmlSerializer(typeof(Test));
                FileStream fs = new FileStream(@"c:\t.xml",FileMode.Create);
                xml.Serialize(fs, t);
                fs.Close();

    反序列化

    FileStream fs = new FileStream(@"c:\t.xml", FileMode.Open);
                XmlSerializer xml = new XmlSerializer(typeof(Test));
                Test t = (Test)xml.Deserialize(fs);

    XmlSerializer类和前两个主流的序列化类的几个不同点是:
      1、不需要Serializable属性,Serializable和NonSerializable属性将会被忽略,但是使用XmlIgnore属性,和NonSerializable属性类似。
      2、该类不能安全地访问私有变成员,所以学要将私有成员改为公共成员,或者提供合适的公共特性。
      3、要求被序列化的类要有一个默认的构造器。
    三 SOAPSeriable

    using System.Runtime.Serialization.Formatters.Soap;

     //序列化
                SoapFormatter soap = new SoapFormatter();
                FileStream fs = new FileStream(@"c:\test.xml", FileMode.Create);
                soap.Serialize(fs,new Test());
                fs.Close();
                Console.WriteLine("成功");
                //反序列化
                FileStream fs1= new FileStream(@"c:\test.xml", FileMode.Open);
                Test t = (Test)soap.Deserialize(fs1);
                Console.WriteLine(t.password);
                fs1.Close();

    二进制序列化的优点:

      1. 所有的类成员(包括只读的)都可以被序列化;

      2. 性能非常好。

      XML序列化的优点:

      1. 互操作性好;

      2. 不需要严格的二进制依赖;

      3. 可读性强。

      通过分析上面的代码,我们知道了选择二进制序列化的方式还是选择XML序列化的方式仅仅是对不同的格式器进行选择而已。你可以根据实际的需要选择相应的格式器完成序列化和反序列化工作。同时请注意,代码中的序列化函数和反序列化函数仅仅是在调用Serialize()和Deserialize()这两个核心函数上产生了差别,即它们的参数不同。因此以上的代码完成了一些最最基本但是很重要的功能,你可以将它们运用在你的程序中,或是将其进行适当扩充以满足程序的特定需要。

     DotNet中的序列化方法有三种: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;
            }

     

  • 相关阅读:
    C#与C++与互操作
    WPF GridView动态添加项并读取数据
    C#使用消息队列(MSMQ)
    使用代码浏览WPF控件模版
    PowerShell将运行结果保存为文件
    opencv + ffmpeg
    vmware
    HttpComponents Downloads
    pytorch 安装
    opencv 3.4.0 的编译
  • 原文地址:https://www.cnblogs.com/millen/p/1768816.html
Copyright © 2011-2022 走看看