Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SerializationBinder with List<T>

I'm trying to make the BinaryFormatter work across different versions of my assembly. The actual class I want to deserialize to is exactly the same in each assembly version, but on deserialization, because the objects are serialized include the assembly name they came from, the BinaryFormatter is complaining that it can't find the right assembly. So I created a custom SerializationBinder that tells the BinaryFormatter to always deserialize to the current assembly version.

My scheme works and can deserialize objects correctly, but it doesn't work if my object is a List of T, where T was a type serialized from an older version of my assembly.

Is there some way to make this work with Lists and other generic types where the type parameter is a class from my assembly?

//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);
}
like image 800
dan Avatar asked Apr 26 '11 18:04

dan


2 Answers

If you serialized an instance of List< MyClass > from your version 1.0.0.0 assembly, the SerializationBinder.BindToType function will be asked to provide this type:

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

In order to remap the List< MyClass > type to your version 2.0.0.0 assembly, you need to change the type name to this:

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

The main point to note, is that the assembly name is not fully qualified. If you try to fully qualify the assembly name with a 2.0.0.0 version number, it will not work.

like image 86
tbergelt Avatar answered Sep 25 '22 07:09

tbergelt


Application A creates a Serialized Binary formatter file "SerializedList.bin" which contains List(Result) where Result is the Serializable object. Now Application B wants to DeSerialize the file and load into List(Result) object. This is how I got it working..

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

Application A Assembly Name is "Serialize"
Application B Assembly Name is "DeSerialize"

Application A Code(Serialize):

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

}

Some Result Object:

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

Application B Code (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));

    }
}    

}

like image 29
devendar r mandala Avatar answered Sep 22 '22 07:09

devendar r mandala