11

我正在尝试BinaryFormatter跨不同版本的程序集进行工作。我要反序列化的实际类在每个程序集版本中完全相同,但是在反序列化时,因为对象被序列化包括它们来自的程序集名称,所以BinaryFormatter抱怨它找不到正确的程序集。所以我创建了一个自定义SerializationBinder,告诉BinaryFormatter总是反序列化到当前的程序集版本。

我的方案可以正常工作并且可以正确反序列化对象,但如果我的对象是 T 的列表,则它不起作用,其中 T 是从我的程序集的旧版本序列化的类型。

有什么方法可以使它与列表和其他泛型类型一起工作,其中类型参数是我的程序集中的一个类?

//the object i want to deserialize
class MyObject
{
     public string Name{get;set;}
}

//my binder class
class MyBinder : SerializationBinder
{
    static string assemblyToUse = typeof (MyObject).Assembly.FullName;
    public override Type BindToType(string assemblyName, string typeName)
    {
        var isMyAssembly = assemblyName.StartsWith("oldAssemblyName");
        var assemblyNameToUse = isMyAssembly ? assemblyToUse : assemblyName;
        var tn = typeName + ", " + assemblyNameToUse;
        return Type.GetType(tn);            
    }
}


//my deserialize method
static object BinaryDeserialize(string input)
{
    var arr = Convert.FromBase64String(input);
    var ms = new MemoryStream(arr);
    ms.Seek(0, SeekOrigin.Begin);
    var bf = new BinaryFormatter();
    bf.Binder = new MyBinder();
    var obj = bf.Deserialize(ms);

    return obj;
}

static void Test()
{
    //this works
    //serialized(new MyObject());
    var str = ReadSerialized_MyObject();  
    var obj = BinaryDeserialize(str);

    //this doesn't work
    //serialized(new List<MyObject>());
    var str2 = ReadSerialized_List_of_MyObject(); 
    var obj = BinaryDeserialize(str2);
}
4

4 回答 4

7

如果您从版本 1.0.0.0 程序集序列化 List<MyClass> 的实例,将要求 SerializationBinder.BindToType 函数提供此类型:

System.Collections.Generic.List`1[[MyAssembly.MyClass, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=12345678901234567890]]

为了将 List<MyClass> 类型重新映射到您的版本 2.0.0.0 程序集,您需要将类型名称更改为:

System.Collections.Generic.List`1[[MyAssembly.MyClass, MyAssembly]]

需要注意的要点是程序集名称不是完全限定的。如果您尝试使用 2.0.0.0 版本号完全限定程序集名称,它将不起作用。

于 2011-11-04T20:16:38.390 回答
1

应用程序 A 创建一个序列化二进制格式化程序文件“SerializedList.bin”,其中包含 List(Result),其中 Result 是可序列化对象。现在应用程序 B 想要反序列化文件并加载到 List(Result) 对象中。这就是我让它工作的方式..

参考:http ://social.msdn.microsoft.com/forums/en-US/netfxremoting/thread/eec2b7a6-65f8-42d1-ad4f-409f46bdad61

应用程序 A 程序集名称为“Serialize”
应用程序 B 程序集名称为“DeSerialize”

应用程序代码(序列化):

namespace Serialize
{
class Program
{
    static void Main(string[] args)
    {            
        List<Result> result = ;//Get From DB

        IFormatter formatter = new BinaryFormatter();
        Stream sStream = new FileStream(
            "SerializedList.bin",
            FileMode.CreateNew,
            FileAccess.Write,
            FileShare.None);

        formatter.Serialize(sStream, result);
        sStream.Close();           
    }
}

}

一些结果对象:

[Serializable]
public class Result
{
    public decimal CONTACT_ID { get; set; }
    public decimal INSTITUTION_NBR { get; set; }
}

应用程序 B 代码(反序列化):

namespace DeSerialize
{
class Program
{
    static void Main(string[] args)
    {
        IFormatter formatter = new BinaryFormatter();

        string fromTypeName = "System.Collections.Generic.List`1[[Serialize.Result, Serialize, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]";
        string fromTypeName1 = "Serialize.Result";

        string toTypename = "System.Collections.Generic.List`1[DeSerialize.Result]";
        string toTypename1 = "DeSerialize.Result";
        string toTypeAssemblyName = Assembly.GetExecutingAssembly().FullName;

        DictionarySerializationBinder dic = new DictionarySerializationBinder();
        dic.AddBinding(fromTypeName, toTypename);
        dic.AddAssemblyQualifiedTypeBinding(fromTypeName1, toTypename1, toTypeAssemblyName);

        formatter.Binder = dic;

        Stream dStream = new FileStream(
            "SerializeList.bin",
            FileMode.Open,
            FileAccess.Read,
            FileShare.Read);

        List<Result> listDS =
            (List<Result>)formatter.Deserialize(dStream);

        dStream.Close();
    }
}

sealed class DictionarySerializationBinder : SerializationBinder
{
    Dictionary<string, Type> _typeDictionary = new Dictionary<string, Type>();

    public override Type BindToType(string assemblyName, string typeName)
    {
        Type typeToReturn;

        if (_typeDictionary.TryGetValue(typeName, out typeToReturn))
        {
            return typeToReturn;
        }

        else
        {
            return null;
        }
    }

    public void AddBinding(string fromTypeName, string toTypeName)
    {

        Type toType = Type.GetType(toTypeName);

        if (toType == null)
        {
            throw new ArgumentException(string.Format(
            "Help, I could not convert '{0}' to a valid type.", toTypeName));
        }

        _typeDictionary.Add(fromTypeName, toType);

    }

    public void AddAssemblyQualifiedTypeBinding(string fromTypeName, string toTypeName, string toTypeAssemblyName)
    {

        Type typeToSerializeTo = GetAssemblyQualifiedType(toTypeAssemblyName, toTypeName);

        if (typeToSerializeTo == null)
        {

            throw new ArgumentException(string.Format(

            "Help, I could not convert '{0}' to a valid type.", toTypeName));

        }

        _typeDictionary.Add(fromTypeName, typeToSerializeTo);

    }

    private static Type GetAssemblyQualifiedType(string assemblyName, string typeName)
    {

        return Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));

    }
}    

}

于 2013-05-20T20:06:12.863 回答
0

调用方法时只需处理AppDomain.AssemblyResolve事件并返回所需的程序集。Type.GetType就这么简单!

于 2018-11-29T10:51:08.067 回答
0

您还可以在 BindToType 函数中使用更简单的构造:

var tn = typeof(List<MyClass>).AssemblyQualifiedName;
return Type.GetType(tn, true)

如果您的类型定义不正确,最后一个参数“true”将为您提供错误消息(带有跟踪)。它会在以后的调试中为您节省大量时间。

于 2020-05-25T06:22:13.833 回答