zoukankan      html  css  js  c++  java
  • 序列化

    为什么需要序列化

    1.应用程序的状态可以保存在一个磁盘或者文件中,下次使用时可以恢复;

    2.对象可以轻松的复制;

    3.对象可以这样进行克隆;

    4.网络发送/跨应用程序域 可以进行加密,压缩等;

    .NET提供的序列化器

    BinaryFormatter(序列化所有的字段,包括私有的)

    XmlSerialization(不能序列化私有字段)(测试了一次发现它竟然可以序列化私有字段,还没找到原因,后面需要验证,求指点)

    DataContractSerializer(WCF通信时的序列化)

    原理

          通过反射来查看每个对象的类型中有哪些实例字段,这些字段引用了另外的其他对象,序列化器就知道其他对象也要被序列化,如果有相互引用,序列化器会检测到这一点,只序列化一遍,不会陷入到死循环。如果某个类的对象可以被序列化,而这个类里面引用了另外一个不可序列化的对象,那么在序列化的过程中就会报错。

    用序列化实现深复制

    //使用序列化来实现深复制(引自《CLR via C#》)
    private static object DeepClone(object original)
    {
        using (MemoryStream stream = new MemoryStream())
        {
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Context = new StreamingContext(StreamingContextStates.Clone);
     
            formatter.Serialize(stream,original);
     
            stream.Position = 0;
     
            return formatter.Deserialize(stream);
        }
    }

    序列化多个对象

          可以用一个序列化器同时序列化多个对象,但是反序列化的时候,也要按照序列化的顺序来先进先出,如果类型不一致,就会抛出异常;在序列化之前,序列化器不会检查对象是否可以序列化(是为了提高性能,所以不检查),所以在序列化的过程中可能会序列化失败而抛出异常,序列化失败时,可能一些对象已经序列化进行了,所以流中就包含了一些损坏的数据。有一种方案可以是先序列化在MemoryStream里面,如果所有的都序列化成功了,再将MemoryStream流里面的内容拷贝到你真正希望的目标流中。

    使类型可序列化

          如果想让某个类的对象可序列化,需要在类名上面加上[Serializable],这个Attribute不能被继承,所以如果你要想让以后子类能够序列化,那么父类一定要加上这些属性。这就是为什么System.Object已经被标记为可序列化了。对于二进制序列化器,序列化时,会序列化所有的字段,不管公有还是私有,Xml序列化器不序列化私有字段。注意:静态字段本身就是类拥有的,它不属于任何对象,我们序列化的是对象的属性,所以它是不能进行序列化;

    控制序列化过程

         可以通过下面的几个Attribute来控制序列化的过程(前两个Attribute是应用在属性上,后四个Attribute是应用在方法上):

    NonSerialized:如果想让某个对象的某个字段不被序列化,而其他的字段都被序列化,那么可以在这个字段上应用这个Attribute。一般包含敏感信息的字段,比如密码等字段都需要加上这个Attribute。

    OptionalField:如果某个类序列化后,又新增加了一些字段,那么在反序列化时,因为找不到新增的字段信息,反序列化时会失败。这时,可以在新增的字段上面应用这个Attribute,那么反序列化时就会忽略这些字段,可以正常的反序列化了。

    OnSerializing:如果想在序列化前做一些其他的初始化工作,那么可以在相应的方法上面加上这个Attribute。

    OnSerialized:如果想在序列化后做一些其他的工作,那么可以在相应的方法上面加上这个Attribute。

    OnDeserializing:如果想在反序列化前做一些其他的初始化工作,那么可以在相应的方法上面加上这个Attribute。

    OnDeserialized:如果想在反序列化后做一些其他的工作,那么可以在相应的方法上面加上这个Attribute。

    序列化到文件

    假设serializeObj是需要被序列化的对象,使用BinaryFormatter序列化到文件: XmlSerialization(不能序列化私有字段)

    FileStream fs = new FileStream(@"D:\1.dat", FileMode.Create);
     
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(fs, serializeObj);//如果serializeObj类里面引用了别的对象,而别的对象又没有标记为可序列化,那么就会在这个地方报错
    fs.Close();
    MessageBox.Show("序列化成功","提示",MessageBoxButtons.OK,MessageBoxIcon.Information);

    反序列化:

    T serializeObj = new T();
    FileStream fs = new FileStream(@"D:\1.dat", FileMode.Open);
    BinaryFormatter bf = new BinaryFormatter();
    serializeObj = (T)bf.Deserialize(fs);

    使用XmlSerialization序列化到文件:

    string filePath = @"D:\1.xml";
    using (System.IO.StreamWriter writer = new System.IO.StreamWriter(filePath))
    {
        System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(serializeObj.GetType());
        xs.Serialize(writer, serializeObj);
        writer.Close();
    }

    反序列化:

    string filePath = @"D:\1.xml";
    T serializeObj=new T();
    if (System.IO.File.Exists(filePath))
    {
        using (System.IO.StreamReader reader = new System.IO.StreamReader(filePath))
        {
            System.Xml.Serialization.XmlSerializer xs = new System.Xml.Serialization.XmlSerializer(serializeObj.GetType());
            object obj = xs.Deserialize(reader);
            reader.Close();
            serializeObj= obj as T;
        }
    }

    其他

       1. 在序列化的类中最好不要用自动属性,因为如果使用了自动属性,那么编译器每次都会为这个自动属性生成一个私有字段,这个私有字段的名字是编译器生成的,并且每次生成的可能都不一样。所以如果使用了自动属性,可能在反序列化的时候就会出错。(但我经过试验,发现没有报错,可以正常的反序列化)

       2. 如果想实现更高级的序列化,可以实现ISerializable接口。

  • 相关阅读:
    Java并发专题 带返回结果的批量任务执行
    angualejs
    Java并发编程:Callable、Future和FutureTask
    mybatis
    InitialContext和lookup
    git 常用使用命令
    junit spring 测试
    redis windows
    为何PS出的RSS总和大于实际物理内存
    32位机器的LowMemory
  • 原文地址:https://www.cnblogs.com/xiaoxiangfeizi/p/2992696.html
Copyright © 2011-2022 走看看