【问题标题】:Deserializing derived classes with custom SerializationBinder使用自定义 SerializationBinder 反序列化派生类
【发布时间】:2014-10-21 09:17:08
【问题描述】:

我正在尝试反序列化一些使用旧版本应用程序序列化的对象,以便将它们升级到我在新版本应用程序中使用的新格式。

为了做到这一点,我使用自定义 SerializationBinder 来将旧对象映射到新对象。

我可以通过这种方式迁移我的大部分对象,但是当我的其中一个对象派生自基类时,我遇到了问题。问题是基类中的属性不会被反序列化(只有派生类中的属性会被反序列化)。

我能够将问题缩小为一个简短的独立程序,我将在此处粘贴:

namespace SerializationTest
{
class Program
{
    static void Main(string[] args)
    {
        v1derived first = new v1derived() { a = 1, b = 2, c = 3, d = 4 };
        v2derived second = null;

        BinaryFormatter bf = new BinaryFormatter();
        bf.Binder = new MyBinder();

        MemoryStream ms = new MemoryStream();
        bf.Serialize(ms, first);
        ms.Seek(0, SeekOrigin.Begin);
        second = (v2derived)bf.Deserialize(ms);
        Console.WriteLine("a={0} b={1} c={2} d={3}", second.a, second.b, second.c, second.d);
    }
}

class MyBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        if (typeName == "SerializationTest.v1base")
        {
            return typeof(v2base);
        }
        if (typeName == "SerializationTest.v1derived")
        {
            return typeof(v2derived);
        }
        return null;
    }
}

[Serializable]
class v1base
{
    public int a { get; set; }
    public int b { get; set; }
}

[Serializable]
class v1derived : v1base
{
    public int c { get; set; }
    public int d { get; set; }
}

[Serializable]
class v2base
{
    public int a { get; set; }
    public int b { get; set; }
}

[Serializable]
class v2derived : v2base
{
    public int c { get; set; }
    public int d { get; set; }
}
}

在这个程序中,我正在序列化一个 v1 派生对象,并尝试将其反序列化为一个 v2 派生对象。两个对象完全相同,但程序不会反序列化 ab 属性。

这是我得到的输出: a=0 b=0 c=3 d=4

我认为问题与自动属性有关。如果我删除 {get;set;} 并将它们变成字段,那么它将起作用。但是我的应用程序中的 v1 对象是属性,所以我必须使用它。

所以问题是:我怎样才能让这个反序列化正常工作?

【问题讨论】:

    标签: c# .net serialization binary-serialization


    【解决方案1】:

    您应该为新版本类型提供反序列化构造函数并实现 ISerializable。 可以使用帮助类 SerializationHelper 从 SerializationInfo 访问旧版本成员:

    static class SerializationHelper
    {
        public static string GetAutoPropertyName(string baseTypeName, string name)
        {
            return baseTypeName + "+<" + name + ">k__BackingField";
        }
    
        public static string GetAutoPropertyName(string name)
        {
            return "<" + name + ">k__BackingField";
        }
    }
    
    [Serializable]
    class v2base : ISerializable
    {
        protected v2base(
            SerializationInfo info,
            StreamingContext context)
        {
            a = info.GetInt32(SerializationHelper.GetAutoPropertyName("v1base", "a"));
            b = info.GetInt32(SerializationHelper.GetAutoPropertyName("v1base", "b"));
        }
    
        public int a { get; set; }
        public int b { get; set; }
    
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue(SerializationHelper.GetAutoPropertyName("v1base", "a"), a);
            info.AddValue(SerializationHelper.GetAutoPropertyName("v1base", "b"), b);
        }
    }
    
    [Serializable]
    class v2derived : v2base
    {
        protected v2derived(
            SerializationInfo info,
            StreamingContext context) : base(info, context)
        {
            c = info.GetInt32(SerializationHelper.GetAutoPropertyName("c"));
            d = info.GetInt32(SerializationHelper.GetAutoPropertyName("d"));
        }
    
        public int c { get; set; }
        public int d { get; set; }
    
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);
            info.AddValue(SerializationHelper.GetAutoPropertyName("c"), c);
            info.AddValue(SerializationHelper.GetAutoPropertyName("c"), d);
        }
    }
    

    【讨论】:

    • 这似乎可行,但有没有办法在不修改 v1basev1derived 类的情况下做到这一点?我需要在一个较大的应用程序中执行此操作,我想在其中迁移一些旧对象,并且我无法更改它们的定义。
    • 我做了更多的挖掘,看起来你可以在不修改 v1basev1derived 的情况下获得这些值。您需要将 v2base 和 v2derived 的序列化构造函数中的调用更改为类似这样,它将起作用:a = info.GetInt32("v1base+&lt;a&gt;k__BackingField");(b、c 和 d 相同)
    • 是的,我刚才也这样做了 :) 有趣,我以前从未这样做过
    • 我也没有。今天我学到的关于二进制序列化的知识比我想知道的要多得多。好在我正在迁移到 xml 序列化。如果您将此内容添加到您的答案中,我会将其标记为已接受。
    猜你喜欢
    • 2012-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多