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);
}
Ответы
Ответ 1
Если вы сериализовали экземпляр 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]]
Чтобы переназначить список <MyClass> для сборки версии 2.0.0.0, вам нужно изменить имя типа на это:
System.Collections.Generic.List`1[[MyAssembly.MyClass, MyAssembly]]
Главное, чтобы отметить, что имя сборки не полностью соответствует требованиям. Если вы попытаетесь полностью квалифицировать имя сборки с номером версии 2.0.0.0, это не сработает.
Ответ 2
Приложение A создает сериализованный двоичный файл форматирования "SerializedList.bin", который содержит список (результат), где Result - объект Serializable.
Теперь приложение B хочет десериализовать файл и загрузить объект List (Result).
Вот как я начал работать.
Ссылка: http://social.msdn.microsoft.com/forums/en-US/netfxremoting/thread/eec2b7a6-65f8-42d1-ad4f-409f46bdad61
Приложение A Название сборки - "Сериализация"
Приложение 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));
}
}
}