.NET序列化与反序列化

一、什么序列化和反序列化

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

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

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

  • 将应用程序的状态保存在一个磁盘文件或数据库中,并在应用程序下次运行时恢复状态。例如, Asp.net 中利用序列化和反序列化来保存和恢复会话状态。
  • 一组对象可以轻松复制到Windows 窗体的剪贴板中,再粘贴回同一个或者另一个应用程序。
  • 将对象按值从一个应用程序域中发送到另一个程序域

并且如果把对象序列化成内存中的字节流,就可以利用一些其他的技术来处理数据,例如,对数据进行加密和压缩等。

二、序列化和反序列简单使用

.Net Framework 提供二种序列化方式:

  • 二进制序列化
  • XML 和SOAP序列化
    demo
    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 = "Come";
                person.personHeight = "175CM";
                person.PersonAge = 23;
                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的其他成员都能序列化,注意这个属性只能应用于一个类型中的字段,而且会被派生类型继承。

    SOAP 和XML 的序列化和反序列化和上面类似,只需要改下格式化器就可以了, 这里我就不列出来了。


    三、控制序列化和反序列化

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

    • 通过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,OnSerializedAttributeOnDeserializingAttribute这些定制属性。

     

    实现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方法的完全一致。

posted on 2012-07-11 10:53  SplendidMe  阅读(307)  评论(0编辑  收藏  举报