.Net中的序列化和反序列化详解

序列化通俗地讲就是将一个对象转换成一个字节流的过程,这样就能够轻松保存在磁盘文件或数据库中。反序列化是序列化的逆过程,就是将一个字节流转换回原来的对象的过程。数据库

然而为何须要序列化和反序列化这样的机制呢?这个问题也就涉及到序列化和反序列化的用途了,数组

对于序列化的主要用途有:加密

1)、将应用程序的状态保存在一个磁盘文件或数据库中,并在应用程序下次运行时恢复状态。例如, Asp.net 中利用序列化和反2)、序列化来保存和恢复会话状态。
3)、一组对象能够轻松复制到Windows 窗体的剪贴板中,再粘贴回同一个或者另外一个应用程序。
将对象按值从一个应用程序域中发送到另外一个程序域
而且若是把对象序列化成内存中的字节流,就能够利用一些其余的技术来处理数据,例如,对数据进行加密和压缩等。spa

序列化和反序列化的简单使用:.net

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
 
namespace Serializable
{
  [Serializable]
  public class Person
  {
   public string personName;
 
   [NonSerialized]
   public string personHeight;
 
   private int personAge;
   public int PersonAge
   {
    get { return personAge; }
    set { personAge = value; }
   }
 
   public void Write()
   {
    Console.WriteLine( "Person Name: " +personName);
    Console.WriteLine( "Person Height: " +personHeight);
    Console.WriteLine( "Person Age: " + personAge);
   }
   
  }
  class Program
  {
   static void Main( string [] args)
   {
    Person person = new Person();
    person.personName = "Jerry" ;
    person.personHeight = "175CM" ;
    person.PersonAge = 22;
    Stream stream = Serialize(person);
 
    //为了演示,都重置
    stream.Position = 0;
    person = null ;
 
    person = Deserialize(stream);
    person.Write();
    Console.Read();
    
   }
   private static MemoryStream Serialize(Person person)
   {
    MemoryStream stream = new MemoryStream();
 
    // 构造二进制序列化格式器
    BinaryFormatter binaryFormatter = new BinaryFormatter();
    // 告诉序列化器将对象序列化到一个流中
    binaryFormatter.Serialize(stream, person);
 
    return stream;
 
   }
 
   private static Person Deserialize(Stream stream)
   {
    BinaryFormatter binaryFormatter = new BinaryFormatter();
    return (Person)binaryFormatter.Deserialize(stream);
   }
   
  }
}
主要是调用System.Runtime.Serialization.Formatters.Binary命名空间下的BinnaryFormatter类来进行序列化和反序列化,

从中能够看出除了标记NonSerialized的其余成员都能序列化,注意这个属性只能应用于一个类型中的字段,并且会被派生类型继承。code

SOAP 和XML 的序列化和反序列化和上面相似,只须要改下格式化器就能够了, 这里我就不列出来了。orm

3、控制序列化和反序列化
  有两种方式来实现控制序列化和反序列化:对象

经过OnSerializing, OnSerialized,OnDeserializing, OnDeserialized,NonSerialized和OptionalField等属性
实现System.Runtime.Serialization.ISerializable接口
第一种方式实现控制序列化和反序列化代码:继承

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
 
namespace ControlSerialization
{
   [Serializable]
   public class Circle
   {
     private double radius; //半径
     [NonSerialized]
     public double area; //面积
 
     public Circle( double inputradiu)
     {
       radius = inputradiu;
       area = Math.PI * radius * radius;
     }
 
     [OnDeserialized]
     private void OnDeserialized(StreamingContext context)
     {
       area = Math.PI * radius * radius;
     }
 
     public void Write()
     {
       Console.WriteLine( "Radius is: " + radius);
       Console.WriteLine( "Area is: " + area);
     }
   }
   class Program
   {
     
     static void Main( string [] args)
     {
       Circle c = new Circle(10);
       MemoryStream stream = new MemoryStream();
       BinaryFormatter formatter = new BinaryFormatter();
       // 将对象序列化到内存流中,这里能够使用System.IO.Stream抽象类中派生的任何类型的一个对象, 这里我使用了 MemoryStream类型。
       formatter.Serialize(stream,c);
       stream.Position = 0;
       c = null ;
       c = (Circle)formatter.Deserialize(stream);
       c.Write();
       Console.Read();
 
     }
   }
}

注意:若是注释掉 OnDeserialized属性的话,area字段的值就是0了,由于area字段没有被序列化到流中。 接口

在上面须要序列化的对象中,格式化器只会序列化对象的radius字段的值。area字段中的值不会序列化,由于该字段已经应用了NonSerializedAttribute属性,而后咱们用Circle c=new Circle(10)这样代码构建一个Circle对象时,在内部,area会设置一个约为314.159这样的值,这个对象序列化时,只有radius的字段的值(10)写入流中, 但当反序列化成一个Circle对象时,它的area字段的值会初始化为0,而不是约314.159的一个值。为了解决这样的问题,因此自定义一个方法应用OnDeserializedAttribute属性。此时的执行过程为:每次反序列化类型的一个实例,格式化器都会检查类型中是否认义了 一个应用了该attribute的方法,若是是,就调用该方法,调用该方法时,全部可序列化的字段都会被正确设置。除了OnDeserializedAttribute这个定制attribute,system.Runtime.Serialization命名空间还定义了OnSerializingAttribute,OnSerializedAttribute和OnDeserializingAttribute这些定制属性。

实现ISerializable接口方式控制序列化和反序列化代码: 

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;
 
namespace ControlSerilization2
{
   [Serializable]
   public class MyObject : ISerializable
   {
     public int n1;
     public intn2;
 
     [NonSerialized]
     public String str;
 
     public MyObject()
     {
     }
 
     protected MyObject(SerializationInfo info, StreamingContext context)
     {
       n1 = info.GetInt32( "i" );
       n2 = info.GetInt32( "j" );
       str = info.GetString( "k" );
     }
 
     [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true )]
     public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
     {
       info.AddValue( "i" , n1);
       info.AddValue( "j" , n2);
       info.AddValue( "k" , str);
     }
 
     public void Write()
     {
       Console.WriteLine( "n1 is: " + n1);
       Console.WriteLine( "n2 is: " + n2);
       Console.WriteLine( "str is: " + str);
     }
   }
 
   class Program
   {
     static void Main( string [] args)
     {
       MyObject obj = new MyObject();
       obj.n1 = 2;
       obj.n2 = 3;
       obj.str = "Jeffy" ;
       MemoryStream stream = new MemoryStream();
       BinaryFormatter formatter = new BinaryFormatter();
       // 将对象序列化到内存流中,这里能够使用System.IO.Stream抽象类中派生的任何类型的一个对象, 这里我使用了 MemoryStream类型。
       formatter.Serialize(stream, obj);
       stream.Position = 0;
       obj = null ;
       obj = (MyObject)formatter.Deserialize(stream);
       obj.Write();
       Console.Read();
     }
   }
}
此时的执行过程为:当格式化器序列化对象时,会检查每一个对象,若是发现一个对象的类型实现了ISerializable接口,格式化器会忽视全部定制属性,改成构造一个新的System.Runtime.Serialization.SerializationInfo对象,这个对象包含了要实际为对象序列化的值的集合。构造好并初始化好SerializationInfo对象后,格式化器调用类型的GetObjectData方法,并向它传递对SerializationInfo对象的引用,GetObjectData方法负责决定须要哪些信息来序列化对象,并将这些信息添加到SerializationInfo对象中,经过调用AddValue方法来添加须要的每一个数据,添加好全部必要的序列化信息后,会返回至格式化器,而后格式化器获取已经添加到SerializationInfo对象中的全部值,并将它们都序列化到流中,当反序列化时,格式化器从流中提取一个对象时,会为新对象分配内存,最初,这个对象的全部字段都设为0或null,而后,格式化器检查类型是否实现了ISerializable接口,若是存在这个接口, 格式化器就尝试调用一个特殊构造器,它的参数和GetObjectData方法的彻底一致。

4、格式化器如何序列化和反序列化
从上面的分析中能够看出,进行序列化和反序列化主要是格式化器在工做的,然而下面就是要讲讲格式化器是如何序列化一个应用了 SerializableAttribute 属性的对象。

一、格式化器调用FormatterServices的GetSerializableMembers方法:public static MemberInfo[] GetSerializableMembers(Type type,StreamingContext context);这个方法利用发射获取类型的public和private实现字段(标记了NonSerializedAttributee属性的字段除外)。方法返回由MemberInfo对象构成的一个数组,其中每一个元素对应于一个可序列化的实例字段。
二、对象被序列化,System.Reflection.MemberInfo对象数组传给FormatterServices的静态方法GetObjectData: public static object[] GetObjectData(Object obj,MemberInfo[] members);  这个方法返回一个Object数组,其中每一个元素都标识了被序列化的那个对象中的一个字段的值。
三、格式化器将程序集标识和类型的完整名称写入流中。
四、格式化器而后遍历两个数组中的元素,将每一个成员的名称和值写入流中。
接下来是解释格式化器如何自动反序列化一个应用了 SerializableAttribute属性的对象。

一、格式化器从流中读取程序集标识和完整类型名称。
二、格式化器调用FormatterServices的静态方法GetUninitializedObject: public static Object GetUninitializedObject(Type ttype);这个方法为一个新对象分配内存,但不为对象调用构造器。然而,对象的全部字段都被初始化为0或null.
3格式化器如今构造并初始化一个MemberInfo数组,调用FormatterServices的GetSerializableMembers方法,这个方法返回序列化好、如今须要反序列化的一组字段。
四、格式化器根据流中包含的数据建立并初始化一个Object数组。
五、将对新分配的对象、MemberInfo数组以及并行Object数组的引用传给FormatterServices的静态方法PopulateObjectMembers:
          public static Object PopulateObjectMembers(Object obj,MemberInfo[] members,Object[] data);这个方法遍历数组,将每一个字段初始化成对应的值。

注:格式化如何序列化和反序列对象部分摘自CLR via C#(第三版),写在这里可让初学者进一步理解格式化器在序列化和反序列化过程当中所作的工做。

 写到这里这篇关于序列化和反序列的文章终于结束了, 但愿对朋友有帮助。

相关文章
相关标签/搜索