SerializationBinder со списком‹T›

Я пытаюсь заставить 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);
}

person dan    schedule 26.04.2011    source источник


Ответы (4)


Если вы сериализовали экземпляр List‹ MyClass > из сборки версии 1.0.0.0, функция 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, это не сработает.

person tbergelt    schedule 04.11.2011
comment
это то, что я в итоге сделал ... указывает на вас, так как я забыл ответить на это сам - person dan; 05.11.2011
comment
Это просто может спасти мне жизнь. Отправка этого 1000+ списка занимает около 3 МБ сообщения после SOAPification с использованием конечной точки службы basicHttpBinding WCF. Теперь, когда я вручную кодирую сообщение в двоичном виде, уменьшите его размер примерно до 0,4 МБ. Ты спасатель! - person Aejay; 06.09.2012

Приложение A создает файл формата Serialized Binary «SerializedList.bin», который содержит список (результат), где результат — это сериализуемый объект. Теперь приложение B хочет десериализовать файл и загрузить его в объект List(Result). Вот так у меня заработало..

Ссылка: http://social.msdn.microsoft.com/forums/en-US/netfxremoting/thread/eec2b7a6-65f8-42d1-ad4f-409f46bdad61

Имя сборки приложения A — "Serialize"
Имя сборки приложения B — "DeSerialize"

Код приложения A (сериализация):

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

    }
}    

}

person devendar r mandala    schedule 20.05.2013

Просто обработайте событие AppDomain.AssemblyResolve и верните нужную сборку при вызове метода Type.GetType. Вот так просто!

person Behzad Sedighzadeh    schedule 29.11.2018

Вы также можете использовать более простую конструкцию, подобную этой, в функции BindToType:

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

Последний параметр «true» выдаст вам сообщение об ошибке (с трассировкой), если ваше определение типа неверно. Позже это сэкономит вам много времени при отладке.

person Ильдар Финт    schedule 25.05.2020