.NET如何自定义序列化和反序列化?

对于某些类型,序列化和反序列化往往有一些特殊的操作或逻辑检查需求,这时就需要我们能够主动地控制序列化和反序列化的过程。.NET中提供的Serializable特性帮助我们非常快捷地申明了一个可序列化的类型(因此也就缺乏了灵活性),但很多时候由于业务逻辑的要求,我们需要主动地控制序列化和反序列化的过程。因此,.NET提供了ISerializable接口来满足自定义序列化需求。.

下面的代码展示了自定义序列化和反序列化的类型模板:

[Serializable]
public class MyObject : ISerializable
{
    protected MyObject(SerializationInfo info, StreamingContext context)
    {
        // 在此构造方法中实现反序列化
    }

    public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        // 在此方法中实现序列化
    }
}

如上代码所示,GetObjectData和特殊构造方法都接收两个参数:SerializationInfo 类型参数的作用类似于一个哈希表,通过key/value对来存储整个对象的内容,而StreamingContext 类型参数则包含了流的当前状态,我们可以根据此参数来判断是否需要序列化和反序列化类型独享。

如果基类实现了ISerializable接口,则派生类需要针对自己的成员实现反序列化构造方法,并且重写基类中的GetObjectData方法。

下面通过一个具体的代码示例,来了解如何在.NET程序中自定义序列化和反序列化的过程:

① 首先我们需要一个需要被序列化和反序列化的类型,该类型有可能被其他类型继承

[Serializable]
public class MyObject : ISerializable
{
    private int _number;
    [NonSerialized]
    private string _name;

    public MyObject(int num, string name)
    {
        this._number = num;
        this._name = name;
    }

    public override string ToString()
    {
        return string.Format("整数是:{0}\r\n字符串是:{1}", _number, _name);
    }

    // 实现自定义的序列化
    protected MyObject(SerializationInfo info, StreamingContext context)
    {
        // 从SerializationInfo对象(类似于一个HashTable)中读取内容
        this._number = info.GetInt32("MyObjectInt");
        this._name = info.GetString("MyObjectString");
    }

    // 实现自定义的反序列化
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        // 将成员对象写入SerializationInfo对象中
        info.AddValue("MyObjectInt", this._number);
        info.AddValue("MyObjectString", this._name);
    }
}

② 随后编写一个继承自MyObject的子类,并添加一个私有的成员变量。需要注意的是:子类必须负责序列化和反序列化自己添加的成员变量。

[Serializable]
public class MyObjectSon : MyObject
{
    // 自己添加的成员
    private string _sonName;

    public MyObjectSon(int num, string name)
        : base(num, name)
    {
        this._sonName = name;
    }

    public override string ToString()
    {
        return string.Format("{0}\r\n之类字符串是:{1}", base.ToString(), this._sonName);
    }

    // 实现自定义反序列化,只负责子类添加的成员
    protected MyObjectSon(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        this._sonName = info.GetString("MyObjectSonString");
    }

    // 实现自定义序列化,只负责子类添加的成员
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        info.AddValue("MyObjectSonString", this._sonName);
    }
}

③ 最后编写Main方法,测试自定义的序列化和反序列化

public class Program
{
    public static void Main(string[] args)
    {
        MyObjectSon obj = new MyObjectSon(10086, "Edison Chou");
        Console.WriteLine("初始对象为:");
        Console.WriteLine(obj.ToString());
        // 序列化
        byte[] data = Serialize(obj);
        Console.WriteLine("经过序列化与反序列化之后:");
        Console.WriteLine(DeSerialize(data));

        Console.ReadKey();
    }

    // 序列化对象-BinaryFormatter
    static byte[] Serialize(MyObject p)
    {
        // 使用二进制序列化
        IFormatter formatter = new BinaryFormatter();
        using (MemoryStream ms = new MemoryStream())
        {
            formatter.Serialize(ms, p);
            return ms.ToArray();
        }
    }

    // 反序列化对象-BinaryFormatter
    static MyObject DeSerialize(byte[] data)
    {
        // 使用二进制反序列化
        IFormatter formatter = new BinaryFormatter();
        using (MemoryStream ms = new MemoryStream(data))
        {
            MyObject p = formatter.Deserialize(ms) as MyObject;
            return p;
        }
    }
}

 上述代码的运行结果如下图所示:

.NET如何自定义序列化和反序列化?

从结果图中可以看出,由于实现了自定义的序列化和反序列化,从而原先使用Serializable特性的默认序列化和反序列化算法没有起作用,MyObject类型的所有成员经过序列化和反序列化之后均被完整地还原了,包括申明了NonSerialized特性的成员。