带有List <t> </t>的SerializationBinder

时间:2011-04-26 18:20:10

标签: c# .net serialization binaryformatter

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

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

有没有办法让这个工作与Lists和其他泛型类型一起使用,其中type参数是我的程序集中的一个类?

//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 个答案:

答案 0 :(得分:7)

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

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

为了重新映射List&lt; MyClass&gt;键入您的版本2.0.0.0程序集,您需要将类型名称更改为:

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

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

答案 1 :(得分:1)

应用程序A创建一个序列化二进制格式化程序文件“SerializedList.bin”,其中包含List(Result),其中Result是Serializable对象。 现在,应用程序B想要对文件进行DeSerialize并加载到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代码(DeSerialize):

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));

    }
}    

}

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

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

如果您的类型定义不正确,最后一个参数'true'将给您错误消息(带有跟踪)。这样可以节省您以后的调试时间。