.net中对象序列化技术浅谈

序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。反之,反序列化根据流重新构造对象。此外还可以将对象序列化后保存到本地,再次运行的时候可以从本地文件中“恢复”对象到序列化之前的状态。
在.net中有提供了几种序列化的方式:
二进制序列化
XML序列化
SOAP序列化

二进制序列化
    所谓二进制序列化,指的是对象序列化之后是二进制形式的。二进制序列化是通过BinaryFormatter类来实现的,这个类位于System.Runtime.Serialization.Formatters.Binary命名空间下。

XML序列化
所谓XML序列化,是指对象序列化之后的结果是XML形式的。保存XML序列化是通过XmlSerializer 类来实现的, 这个类位于System.Xml.Serialization命名空间下。


SOAP序列化

所谓SOAP序列化是指对象序列化之后的结果符合SOAP协议,也就是可以通过SOAP协议传输(不知道SOAP协议?百度一下吧)。SOAP序列化是通过SoapFormatter类来实现的,这个类位于System.Runtime.Serialization.Formatters.Soap命名空间下,并且需要注意需要手动添加对这个命名空间的引用,如下图所示:

20101119-211330498

下面编写一个类用于序列化和反序列化,这个类的代码如下:

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Text;   
  4.   
  5. namespace MySerializeDemo   
  6. {   
  7.     [Serializable]   
  8.     ///
      
  9.     /// 要序列化的对象   
  10.     /// 作者:周公   
  11.     /// 编写时间:2009-03-10   
  12.     ///    
  13.     public class MyObject   
  14.     {   
  15.         //[NonSerialized]   
  16.         private string name;   
  17.         private DateTime birthday;   
  18.         private string homePlace;   
  19.         ///
      
  20.         /// 出生地   
  21.         ///    
  22.         public string HomePlace   
  23.         {   
  24.             get { return homePlace; }   
  25.             set { homePlace = value; }   
  26.         }   
  27.     
  28.         ///
      
  29.         /// 生日   
  30.         ///    
  31.         public DateTime Birthday   
  32.         {   
  33.             get { return birthday; }   
  34.             set { birthday = value; }   
  35.         }   
  36.     
  37.         ///
      
  38.         /// 姓名   
  39.         ///    
  40.         public string Name   
  41.         {   
  42.             get { return name; }   
  43.             set { name = value; }   
  44.         }   
  45.     
  46.         ///
      
  47.         /// 年龄   
  48.         ///    
  49.         public int Age   
  50.         {   
  51.             get { return DateTime.Now.Year - birthday.Year; }   
  52.         }   
  53.         ///
      
  54.         /// override了ToString()方法   
  55.         ///    
  56.         ///    
  57.         public override string ToString()   
  58.         {   
  59.             return string.Format("姓名:{0},生日:{1},出生地:{2},年龄:{3}",name,birthday,homePlace,Age);   
  60.         }   
  61.     
  62.     }   
  63. }  

下面是分别用上面的三个类进行序列化和反序列化的代码:

  1. using System;   
  2. using System.Collections.Generic;   
  3. using System.Text;   
  4. using System.IO;   
  5. using System.Runtime.Serialization.Formatters;   
  6. using System.Runtime.Serialization.Formatters.Binary;   
  7. using System.Runtime.Serialization.Formatters.Soap;   
  8. using System.Xml.Serialization;   
  9.   
  10. namespace MySerializeDemo   
  11. {   
  12.     class Program   
  13.     {   
  14.         static void Main(string[] args)   
  15.         {   
  16.             MyObject obj = new MyObject();   
  17.             obj.Birthday = new DateTime(1979, 11, 7);   
  18.             obj.HomePlace = "湖北";   
  19.             obj.Name = "周公";   
  20.             Console.WriteLine("========使用BinaryFormatter类进行序列化和反序列化。====");   
  21.             BinarySerialize(obj);   
  22.             BinaryDeserialize("C:\\MyObject.dat");   
  23.             Console.WriteLine("========使用SoapFormatter类进行序列化和反序列化。====");   
  24.             SOAPSerialize(obj);   
  25.             SOAPDeserialize("C:\\MyObject.soap");   
  26.             Console.WriteLine("========使用XmlSerializer类进行序列化和反序列化。====");   
  27.             XMLSerialize(obj);   
  28.             XMLDeserialize("C:\\MyObject.xml");   
  29.         }   
  30.         ///
      
  31.         /// 二进制序列化对象   
  32.         ///    
  33.         ///    
  34.         public static void BinarySerialize(MyObject obj)   
  35.         {   
  36.             using (FileStream stream = new FileStream("C:\\MyObject.dat", FileMode.Create, FileAccess.Write))   
  37.             {   
  38.                 BinaryFormatter formater = new BinaryFormatter();   
  39.                 formater.Serialize(stream, obj);   
  40.                 Console.WriteLine("对象已经被序列化。" + obj.ToString());   
  41.             }   
  42.         }   
  43.         ///
      
  44.         /// 二进制反序列化   
  45.         ///    
  46.         ///    
  47.         public static void BinaryDeserialize(string fileName)   
  48.         {   
  49.             using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))   
  50.             {   
  51.                 BinaryFormatter formater = new BinaryFormatter();   
  52.                 MyObject obj=(MyObject)formater.Deserialize(stream);   
  53.                 Console.WriteLine("对象已经被反序列化。" + obj.ToString());   
  54.             }   
  55.         }   
  56.   
  57.         ///
      
  58.         /// 二进制序列化对象   
  59.         ///    
  60.         ///    
  61.         public static void SOAPSerialize(MyObject obj)   
  62.         {   
  63.             using (FileStream stream = new FileStream("C:\\MyObject.soap", FileMode.Create, FileAccess.Write))   
  64.             {   
  65.                 SoapFormatter formater = new SoapFormatter();   
  66.                 formater.Serialize(stream, obj);   
  67.                 Console.WriteLine("对象已经被序&化。" + obj.ToString());   
  68.             }   
  69.         }   
  70.         ///
      
  71.         /// 二进制反序列化   
  72.         ///    
  73.         ///    
  74.         public static void SOAPDeserialize(string fileName)   
  75.         {   
  76.             using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))   
  77.             {   
  78.                 SoapFormatter formater = new SoapFormatter();   
  79.                 MyObject obj = (MyObject)formater.Deserialize(stream);   
  80.                 Console.WriteLine("对象已经被反序列化。" + obj.ToString());   
  81.             }   
  82.         }   
  83.         ///
      
  84.         /// XML序列化   
  85.         ///    
  86.         ///    
  87.         public static void XMLSerialize(MyObject obj)   
  88.         {   
  89.             using (FileStream stream = new FileStream("C:\\MyObject.xml", FileMode.Create, FileAccess.Write))   
  90.             {   
  91.                 XmlSerializer serializer = new XmlSerializer(typeof(MyObject));   
  92.                 serializer.Serialize(stream, obj);   
  93.                 Console.WriteLine("对象已经被序列化。" + obj.ToString());   
  94.             }   
  95.         }   
  96.         ///
      
  97.         /// XML反序列化   
  98.         ///    
  99.         ///    
  100.         public static void XMLDeserialize(string fileName)   
  101.         {   
  102.             using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))   
  103.             {   
  104.                 XmlSerializer serializer = new XmlSerializer(typeof(MyObject));   
  105.                 MyObject obj = (MyObject)serializer.Deserialize(stream);   
  106.                 Console.WriteLine("对象已经被反序列化。" + obj.ToString());   
  107.             }   
  108.         }   
  109.     }   
  110. }

这个程序的运行效果如下:

20101119-211818186

可见通过上面三个类都能实现将对象序列化保存,并且都能反序列化还原到对象被序列化之前的状态(这正是序列化意义所在,能保存对象运行时的状态并且还能还原)。如果运行上面的代码会在C盘根目录下创建三个文件,分别是MyObject.dat、MyObject.soap和MyObject.xml文件,因为MyObject.dat是二进制文件,所以无法查看文件的内容,但是我们可以打开MyObject.soap和MyObject.xml这两个文件来比较一下有什么区别。
MyObject.soap文件的后缀虽然是.soap,但是还是可以用记事本打开的,下面是MyObject.soap文件的内容:

  1. <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/">  
  2. <SOAP-ENV:Body>  
  3. <a1:MyObject id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/MySerializeDemo/MySerializeDemo%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">  
  4. <name id="ref-3">周公name>  
  5. <birthday>1979-11-07T00:00:00.0000000+08:00birthday>  
  6. <homePlace id="ref-4">湖北homePlace>  
  7. a1:MyObject>  
  8. SOAP-ENV:Body>  
  9. SOAP-ENV:Envelope>

MyObject.xml文件也可以用记事本打开,它的内容如下:

  1. < ?xml version="1.0"?>  
  2. <MyObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">  
  3.   <HomePlace>湖北HomePlace>  
  4.   <Birthday>1979-11-07T00:00:00Birthday>  
  5.   <Name>周公Name>  
  6. MyObject>

熟悉SOAP协议的朋友一看MyObject.soap文件的内容就知道它符合SOAP协议,MyObject.xml文件毫无疑问是一个符合XML规范的文件。

对代码作几点说明:
1、如果采用BinaryFormatter类或者SoapFormatter类来实现序列化,则一定要给类加上Serializable属性,如代码中所示:

  1. [Serializable]
  2.     ///
      
  3.     /// 要序列化的对象   
  4.     /// 作者:周公   
  5.     /// 编写时间:2009-03-10   
  6.     /// 
  7.     public class MyObject

如果不给要序列化的对象加上这个属性,那么采用采用BinaryFormatter类或者SoapFormatter类来实现序列化时会报异常,但使用XmlSerializer 类序列化对象时可以不用这个属性。
2、另外,如果不想序列化某个字段,可以给其加上NonSerialized属性,这样在序列化时就不会保存这个这个字段的值了,比如不想序列化name这个字段,可以如下写代码:

  1. //其它代码   
  2. //[NonSerialized]
  3. private string name;
  4. //其它代码

再次运行刚才的程序会得到如下效果:

20101119-212255858

看有黄色底线部分,因为name字段不被序列化,所以通过二进制序列化和SOAP序列化之后再反序化就得不到原来的值了。
3、最后还需要说明一点的是,SoapFormatter类在.net3.5开始已经过时了,微软建议使用BinaryFormatter类来序列化和反序列化了。

周公
2009-03-11 0:17

本文地址:http://blog.csdn.net/zhoufoxcn/archive/2009/03/11/3978874.aspx


如果给你带来帮助,欢迎微信或支付宝扫一扫,赞一下。