第三节:控制序列化和反序列化

使用SerializableAttribute这个定制attribute应用于一个类型时,所有实例字段都会被序列化。然而,类型可能定义了一些不应序列化的实例字段。一般情况下,有两个原因促使我们不想对类型的部分实例字段进行序列化。

  1. 字段含有反序列化变得无效的信息。例如,假定一个对象包含到一个Window内核对象(如文件、进程、线程、互斥体、事件、信号量等)的句柄,那么在序列化到另一个进程或另一台机器之后,就会失去意义。因为Windows内核对象时跟进程相关的值。
  2. 字段含有很容易计算的信息。在这种情况下,要选出哪些无需序列化的字段,减少需要传输的数据,从而增强应用程序的性能。

以下代码使用了NonSerializableAttribute定制的attribute来指明类型的哪些字段不应序列化。注意,这个attribute也是在System命名空间中定义的。

[Serializable]
    internal class Circle
    {
        private double m_radius;
        [NonSerialized]
        private double m_area;

        public Circle(double radius)
        {
            m_radius = radius;
            m_area = Math.PI * m_radius * m_radius;

        }
    }

在上述代码中,Circle的对象可以序列化。然而,格式化器只会序列化对象的m_radius字段的值。m_area字段中的值不会序列化,因为字段已应用了NonSerializableAttribute。注意,这个attribute只能应用于类型中的一个字段,而且会被派生类继承。当然,可以向一个类型中的多个字段应用NonSerializableAttribute。

假定我们的代码像下面这样构造了一个Circle对象:

Circle c=new Circle(10);

在内部,m_area字段会设置成一个约为314.159的值。这个对象序列化时,只有m_radius字段的值才会写入流。这正是我们希望的,但是当流反序列化一个Circle对象时,就会遇到一个问题。反序列化时,Circle对象的m_radius字段会被设为10,但它的m_area字段会被初始化成0,而不是314.159!

以下代码演示了如何修改Circle类型来修正这个问题:

[Serializable]
    internal class Circle
    {
        private double m_radius;
        [NonSerialized]
        private double m_area;

        public Circle(double radius)
        {
            m_radius = radius;
            m_area = Math.PI * m_radius * m_radius;

        }
        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            m_area = Math.PI * m_radius * m_radius;
        }
    }

在修改过的Circle中,包含一个用System.Runtime.Serialization.OnDeserializedAttribute定制的attribute进行了标记的方法。每次反序列化一个类型的实例,格式化器都会检查类型中是否定义了一个应用了该attribute的方法。如果是,就调用该方法。调用这个方法时,所有可序列化的字段都会被正确设置。在方法中,可能需要访问这些字段来执行一些额外的工作,从而确保对象的完全发序列化。

在上述Circle修改版本中,我调用OnDeserialized方法,使用m_radius字段来计算圆的面积,并将结果放到m_area字段中。这样一来,m_area就有了我们希望的值.

除了OnDeserializedAttribute这个定制attrubite,System.Runtime.Serialization命名空间中还定义了OnSerializingAttribute,OnSerializingAttribute和OnDeserializedAttribute这些定制attribute.可将他们应用于类型中定义的方法,对反序列化和序列化过程进行更多的控制。在下面这个类中,这些attribute被应用于不同的方法:

[Serializable]
    public class MyType
    {
        Int32 x, y;
        [NonSerialized]
        Int32 sum;
        public MyType(Int32 x, Int32 y)
        {
            this.x = x;
            this.y = y;
            sum = x + y;
        }
        [OnDeserializing]
        private void OnDeserializing(StreamingContext context)
        {
            //示例:在这个类型的新版本中,为字段设置默认值
        }
        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            //示例:根据字段初始化瞬时状态
            sum = x + y;
        }
        [OnSerializing]
        private void OnSerializing(StreamingContext context)
        {
            //示例:在序列化钱修改任何需要修改的状态
        }
        [OnSerialized]
        private void OnSerialized(StreamingContext context)
        {
            //示例:在序列化后,恢复任何需要恢复的状态
        }

    }

使用这4个属性中的任何一个时,你定义的方法必须获取一个StreamingContext参数并返回void.方法名可以使你希望的任何名称。另外,应该将方法声明为private,以避免他被普通的代码调用;格式化器运行时有充足的的权限,所以能调用私有方法。

如果序列化一个类型的实例,在类型中添加一个新字段,然后试图反序列化不包含新字段的对象,格式化器会抛出一个SerializationException异常,并显示一条消息告诉你流中要反序列化的数据包含错误的成员数目。这非常不利于版本控制,因为我们经常要在一个类型的版本中添加字段。幸好,这是可以利用System.Runtime.Serialization.OptionalFieldAttribute的帮助。

类型中新增的每个字段都应用一个OptionalFieldAttribute,然后,当格式化器遇到该attribute应用于一个字段时,就不会因为流中的数据不包含这个字段而抛出SerializationException。

注意  序列化一组对象时,格式化器首先调用对象标记了OnSerializing attribute的所有方法。接着,它序列化所有对象的字段。最后,调用对象标记了OnSerialized的所有方法。类似的,反序列化一组对象时,格式化器调用对象标记了OnDeserializing所有方法,然后,它反序列对象的所有字段。最后,它调用对象的标记了OnDeserialized的所有方法。

还要注意,在反序列化期间,当一个格式化器看到一个类型提供的一个方法标记了OnDeserialized时,格式化器会将这个对象的引用添加到一个内部列表中。所有对象都反序列化之后,格式化器以相反的方向遍历这个列别,调用每个对象的OnDeserialized方法。调用这个方法后,所有可序列化的字段都会被正确设置,可访问这些字段来执行任何必要的、进一步的工作,以便将对象完整的反序列化。之所以要以相反的顺序调用这些方法,因为允许内层对象先于外层对象结束反序列化。

例如,假定一个集合对象内部用一个哈希表维护他的数据项列表。集合对象类型可实现一个标记了OnDeserialized方法。即使集合对象先反序列化,它的OnDeserialized方法也会最后调用(在调用完它的数据项的所有OnDeserialized方法之后)。这样一来,所有数据项都反序列化后,他们的所有字段都能得到正确的初始化,以便计算出一个好的哈希码值。然后,集合对象创建它的内部哈希桶、并利用数据项的哈希码将数据项放到桶中。

时间: 2024-12-29 12:04:20

第三节:控制序列化和反序列化的相关文章

第五节:控制序列化和反序列化的数据

本章前面讨论过,控制序列化和反序列化过程的最佳方式就是使用OnSerializing.OnSerialized.OnDeserializing.OnDeserialized.NonSerialized.OptionalField等attribute.然而,在一些极少见的情况下,这些attribute不能提供你希望的全部控制.除此之外,格式化器在内部使用了反射,而反射的速度比较慢,这会增大序列化和反序列化对象所化的时间.为了序列化/反序列化的数据进行完全的控制,并避免使用反射,你的类型可实现ISe

Serializable 指示一个类可以序列化;ICloneable支持克隆,即用与现有实例相同的值创建类的新实例(接口);ISerializable允许对象控制其自己的序列化和反序列化过程(接口)

Serializable : 序列化是指将对象实例的状态存储到存储媒体的过程.在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流.在随后对对象进行反序列化时,将创建出与原对象完全相同的副本. 在 面向对象的环境中实现序列化机制时,必须在易用性和灵活性之间进行一些权衡.只要您对此过程有足够的控制能力,就可以使该过程在很大程度上自动进行.例 如,简单的二进制序列化不能满足需要,或者,由于特定原因需要确定类中那些字段需要序列化. 基本序列化

Java序列化1:序列化、反序列化和transient关键字的作用

网上讲Java序列化的文章很多,感觉很多都讲得不全,这篇文章希望可以全面地剖析Java的序列化机制.为什么要进行序列化和反序列化?我们写了一个Object,但那是Java虚拟机堆内存里面的东西,利用Object进行网络通信.IO操作的时候怎么会认识Java堆内存里面的东西?所以,需要序列化和反序列化机制的保障. 序列化:将一个对象转换成一串二进制表示的字节数组,通过保存或转移这些字节数据来达到持久化的目的. 反序列化:将字节数组重新构造成对象. 默认序列化 序列化只需要实现java.io.Ser

java对象序列化、反序列化

平时我们在Java内存中的对象,是无法进行IO操作或者网络通信的,因为在进行IO操作或者网络通信的时候,人家根本不知道内存中的对象是个什么东西,因此必须将对象以某种方式表示出来,即存储对象中的状态.一个Java对象的表示有各种各样的方式,Java本身也提供给了用户一种表示对象的方式,那就是序列化.换句话说,序列化只是表示对象的一种方式而已.OK,有了序列化,那么必然有反序列化,我们先看一下序列化.反序列化是什么意思. 序列化:将一个对象转换成一串二进制表示的字节数组,通过保存或转移这些字节数据来

java中对象的序列化和反序列化

[对象的序列化和反序列化 ] 1.定义:序列化--将对象写到一个输出流中.反序列化则是从一个输入流中读取一个对象.类中的成员必须是可序列化的,而且要实现Serializable接口,这样的类的对象才能被序列化和反序列化.这个接口是一个表示型的接口.serialVersionUID是一个串行化类的通用标示符,反串行化就是使用这个标示符确保一个加载的类对应一个可串行化的对象. 自己指定了serialVersionUID,就可以在序列化后,去添加一个字段,或者方法,而不会影响到后期的还原,还原后的对象

详细序列化和反序列化笔记

序列化概念: 按值封送:将对象从一个应用程序域传输到另一个应用程序域,居然后反序列化,以便第二个应用程序域中生成该对象的一个精确副本. 如果对象从MarshalByRefObject派生,则将对象引用从一个应用程序域传递到另一个. 自定义序列化的安全问题: 序列化可以允许其他代码查看或修改用其他方式无法访问的对象数据.因此执行序列化的代码需要制定SerializationFormatter标识的SecurityPermission.默认情况下,网络下载的代码不会授予该权限,只有本地计算机上的代码

如何自定义序列化和反序列化

分析问题 在其他很多编程语言(比如C++)中,实现序列化和反序列化的过程往往需要程序员手动地编写代码.而.NET中提供的Serializable特性却帮助程序员非常便捷地申明一个可序列化的类型.但是,便捷的同时往往意味着缺乏灵活性.很多时候,由于业务逻辑的要求,程序员往往需要主动地控制序列化和反序列化的过程,.NET提供了ISerializable接口来满足自定义序列化需求. 为了实现自定义的序列化和反序列化,程序员需要实现ISerializable接口,并且提供一个特定的在反序列化中构造对象的

C#对象序列化与反序列化

1.对象序列化的介绍 (1).NET支持对象序列化的几种方式 二进制序列化:对象序列化之后是二进制形式的,通过BinaryFormatter类来实现的,这个类位于System.Runtime.Serialization.Formatters.Binary命名空间下. SOAP序列化:对象序列化之后的结果符合SOAP协议,也就是可以通过SOAP 协议传输,通过System.Runtime.Serialization.Formatters.Soap命名空间下的SoapFormatter类来实现的.

Java对象表示方式1:序列化、反序列化和transient关键字的作用

http://www.cnblogs.com/xrq730/p/4821958.html 平时我们在Java内存中的对象,是无 法进行IO操作或者网络通信的,因为在进行IO操作或者网络通信的时候,人家根本不知道内存中的对象是个什么东西,因此必须将对象以某种方式表示出来,即 存储对象中的状态.一个Java对象的表示有各种各样的方式,Java本身也提供给了用户一种表示对象的方式,那就是序列化.换句话说,序列化只是表示对 象的一种方式而已.OK,有了序列化,那么必然有反序列化,我们先看一下序列化.反序