Бинарная сериализация наследуемых классов

vb_sub

Всем привет, к сожалению столкнулся со следующей проблемой.У меня есть класс, содержащий список экземпляров других классов Ориентировочно такой:
public class FakeSerializeClass : BaseViewModel
 {
 // неважно какие свойства
 }

 [Serializable]
 public class SingleTonFakeSerialized
 {
 public ObservableCollection<FakeSerializeClass> fake_sz_list { get; set; }
 }
Я хочу его бинарно сериализовать, но у меня валятся ошибки сначала что FakeSerializeClass не помечен как несериализуемый,потом что BaseViewModel несериализуемый, потом что другие вспосмогательные классы, которые каким-либо образом задействованы в логике FakeSerializeClass ниже по иерархии. То есть по сути бинарный сериализатор требует выставить атрибут [Serializable] вплоть до самого верха иерархии всех классов+ всех классов, которые каким-либо образом сопряжены с ним.Но мне нужно сериализовать только SingleTonFakeSerialized, остальные классы не должны быть сериализуемыми.Очень странно, потому что XML-сериализатор сериализовал SingleTonFakeSerialized удачно и не требовал помечать как [Serializable] все родительские классы. Можно ли как-то избежать помечания всех классов как сериализуемые?
8 ответов

vb_sub

vb_sub,это обыкновенное соглашение - контракт, в контексте безопасности.попробуйте другие бинарные исполнители которые работают без это маркера..


vb_sub

потому, что модель представления не предназначена для сериализации.вы подходите к проблеме не с той стороны. Нужно подходить со стороны данных:у меня есть данные, мне сделать сериализацию\десериализацию для них, а не у меня есть класс, мне нужно его сериализовать\десериализовать :)есть модель данных, где это всё реализуется.
Очень странно, потому что XML-сериализатор сериализовал SingleTonFakeSerialized удачно и не требовал помечать как [Serializable] все родительские классы
XML-сериализатор требует помечать в родительском классе атрибутом [XmlInclude(Type)] все наследуемые типы, если что.Сделайте как нормальные люди. Создайте модель данных которая будет сериализовывать\десериализовывать данные в какие то типы. После когда загрузили\десериализовали данные создаете модель представления и передаете этот объект данных туда. В модели управляете этим объектом и если нужно сохраняете его.Таким образом вы отделите мух от котлет и меня модель представления это не будет отражаться на формате данных с которым работает ваша программа, а люди которые потом будут сопровождать это, не будут ломать голову и вспоминать вас плохим словом :)


vb_sub

Roman Mejtes,то есть если XML-сериализатор может сериализовать класс вместе с его внутренней структурой( например вложенные иерархические элементы), то для бинарного необходимо все преобразовать в набор простых типов данных, убрать всю иерархию элементов внутри класса и тд?


vb_sub

vb_sub,Можно извернуться с помощью SurrogateSelector'а:
class Program
{
 static void Main()
 {
 var bar = new Bar
 {
 Name = "bar",
 Zot = 123
 };
 Console.WriteLine(bar);
 byte[] data;
 using(var ms = new MemoryStream())
 {
 var bf = new BinaryFormatter();
 var surrogateTypes = new[] { typeof(Bar) };
 var ss = new SurrogateSelector();
 foreach(var t in surrogateTypes)
 ss.AddSurrogate(t, new StreamingContext(StreamingContextStates.All), new NonSerialiazableTypeSurrogate());
 bf.SurrogateSelector = ss;
 bf.Serialize(ms, bar);
 data = ms.ToArray();
 }
 using (var ms = new MemoryStream(data))
 {
 var bf = new BinaryFormatter();
 var surrogateTypes = new[] { typeof(Bar) };
 var ss = new SurrogateSelector();
 foreach (var t in surrogateTypes)
 ss.AddSurrogate(t, new StreamingContext(StreamingContextStates.All), new NonSerialiazableTypeSurrogate());
 bf.SurrogateSelector = ss;
 var bar2 = bf.Deserialize(ms) as Bar;
 Console.WriteLine(bar2?.ToString() ?? "null");
 
 }
 Console.WriteLine("done");
 Console.ReadKey(true);
 }
}

class Foo
{
 public int Zot { get; set; }
}

[Serializable]
class Bar : Foo
{
 public string Name { get; set; }
 public override string ToString()
 {
 return string.Concat(Name, "#", Zot);
 }
}

internal class NonSerialiazableTypeSurrogate : ISerializationSurrogate
{
 public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
 {
 GetObjectData(obj.GetType(), obj, info);
 }

 static void GetObjectData(Type type, object obj, SerializationInfo info)
 {
 var fieldInfos = type.GetFields(
 BindingFlags.Instance |
 BindingFlags.Public |
 BindingFlags.NonPublic);
 foreach (var fi in fieldInfos)
 if (IsKnownType(fi.FieldType))
 info.AddValue(fi.Name, fi.GetValue(obj));
 else if (fi.FieldType.IsClass)
 info.AddValue(fi.Name, fi.GetValue(obj));
 var bt = type.BaseType;
 if (bt!=typeof(object))
 GetObjectData(bt, obj, info);
 }

 public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
 {
 SetObjectData(obj.GetType(), obj, info);
 return obj;
 }

 static void SetObjectData(Type type, object obj, SerializationInfo info)
 {
 var fieldInfos = type.GetFields(
 BindingFlags.Instance |
 BindingFlags.Public |
 BindingFlags.NonPublic);
 foreach (var fi in fieldInfos)
 {
 if (IsKnownType(fi.FieldType))
 {
 if (IsNullableType(fi.FieldType))
 {
 var argumentValueForTheNullableType = GetFirstArgumentOfGenericType(fi.FieldType);
 fi.SetValue(obj, info.GetValue(fi.Name, argumentValueForTheNullableType));
 }
 else
 fi.SetValue(obj, info.GetValue(fi.Name, fi.FieldType));
 }
 else if (fi.FieldType.IsClass)
 fi.SetValue(obj, info.GetValue(fi.Name, fi.FieldType));
 }
 var bt = type.BaseType;
 if (bt != typeof(object))
 SetObjectData(bt, obj, info);
 }

 static Type GetFirstArgumentOfGenericType(Type type)
 {
 return type.GetGenericArguments()[0];
 }

 static bool IsNullableType(Type type)
 {
 if (type.IsGenericType)
 return type.GetGenericTypeDefinition() == typeof(Nullable<>);
 return false;
 }

 static bool IsKnownType(Type type)
 {
 return type == typeof(string) ||
 type.IsPrimitive ||
 type.IsSerializable;
 }
}
консольный вывод:
bar#123bar#123
Но, как выше писал Роман, у вас налицо смешивание вьюмодели и модели, что не есть хорошо.


vb_sub

вот вам пример, бинарная сериализация, абстрактный класс, с иерархией, всё работаету вас проблема только в том, что вы пытаетесь десериализовывать модель представления, что в корне неверно
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace BinSerialize
{
 [Serializable]
 public abstract class Person
 {
 public string Name { set; get; }
 public int Age { set; get; }
 public Person[] Dependent { set; get; }
 }

 [Serializable]
 public class Employer : Person
 {
 public string Title { set; get; }

 public override string ToString()
 {
 return $"{Name}, {Age}, {Title}";
 }
 }


 [Serializable]
 public class Children : Person
 {
 public DateTime Birthday { set; get; }

 public override string ToString()
 {
 return $"{Name}, {Age}, {Birthday:d}";
 }
 }

 internal class Program
 {
 private static readonly BinaryFormatter BinFormater = new BinaryFormatter();

 private static void Serialize(Person person, string filename)
 {
 using (Stream fStream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
 {
 BinFormater.Serialize(fStream, person);
 }
 }

 private static Person Deserialize(string filename)
 {
 using (Stream fStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None))
 {
 return (Person)BinFormater.Deserialize(fStream);
 }
 }


 static void Main()
 {
 //Создаем объект
 Person person = new Employer
 {
 Name = "Roman Meytes",
 Age = 35,
 Title = "Developer",
 Dependent = new Person[]
 {
 new Children
 {
 Name = "Olga Meytes",
 Age = 8,
 Birthday = new DateTime(2008, 12, 21)
 },
 new Employer
 {
 Name = "Ivan Ivanov",
 Age = 45,
 Title = "Lead developer"
 }
 }
 };
 //Сохраняем\сериализуем в файл
 Serialize(person, @"user.dat");

 //Загружаем из файла объект
 Person employer = Deserialize(@"user.dat");
 //Отображаем на экране
 Console.WriteLine(employer);
 foreach (var child in employer.Dependent)
 {
 Console.Write("\t");
 Console.WriteLine(child);
 }
 Console.WriteLine("Press any key to continue");
 Console.ReadKey();
 }
 }
}
Результат выполнения:
Roman Meytes, 35, Developer Olga Meytes, 8, 12/21/2008 Ivan Ivanov, 45, Lead developer


vb_sub

вот вам пример, бинарная сериализация, абстрактный класс, с иерархией, всё работаету вас проблема только в том, что вы пытаетесь десериализовывать модель представления, что в корне неверно
Ну, если в обычной модели данных в иерархии будут классы без атрибута сериализации, то это не взлетит без доп. усилий.


vb_sub

Сон Веры Павловны,ну очевидно, что атрибут должен быть. если есть тип в модели, у которого такого атрибута нет и он доступен только из готовой сборки, то можно просто обернуть его своим типом, не особо сложная операция, если объект конечно не очень большой.Ну, а если он большой и сложный, то можно создать полностью свою модель, а после загрузки уже инициализировать нужные классы модели.а потом на всё этом создавать модель представления из которой управлять всем этим варевом )


vb_sub

Roman Mejtes,спасибо за пример.