Страницы

Поиск по вопросам

вторник, 12 марта 2019 г.

Сериализация свойства коллекции

Есть такой класс:
public class ChapterCollection : ObservableCollection { public bool? IsSelectedAll { get { return _isSelectedAll; } set { _isSelectedAll = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsSelectedAll))); } } }
При его сериализации в XML свойство IsSelectedAll не сериализуется. Только коллекция объектов ChapterVM
Как сериализовать свойство?
Сериализация происходит так:
public static void SerializeToXml(string path, object saveObject) { var formatter = new XmlSerializer(saveObject.GetType()); using (FileStream fs = new FileStream(path, FileMode.Create)) { formatter.Serialize(fs, saveObject); } }


Ответ

Наиболее простым решением является отказ от наследования в пользу композиции. Действительно, логично предположить что ChapterCollection не просто "является" коллекцией ChapterVM, а содержит в себе коллекцию ChapterVM и также некоторые другие свойства, как признак выделенности элементов коллекции и т.п. Зы. Недаром умные книги по проектированию пишут
Предпочитайте композицию наследованию

Если всё же вы намерены оставить наследование, для переопределения действий сериализатора необходимо реализовать интерфейс IXmlSerializable. Сделать это можно примерно так:
public class ChapterCollection : ObservableCollection, IXmlSerializable { bool? _isSelectedAll; public bool? IsSelectedAll { get { return _isSelectedAll; } set { _isSelectedAll = value; OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsSelectedAll))); } }
public XmlSchema GetSchema() => null;
public void ReadXml(XmlReader reader) { reader.ReadStartElement();
// Читаем из потока дополнительные свойства вручную IsSelectedAll = reader.ReadElementContentAsBoolean(nameof(IsSelectedAll), ""); // Читаем элементы коллекции XmlSerializer ser = new XmlSerializer(typeof(Chapter)); while (reader.NodeType == XmlNodeType.Element) Add((Chapter)ser.Deserialize(reader));
reader.ReadEndElement(); }
public void WriteXml(XmlWriter writer) { // Сериализуем дополнительные свойства вручную writer.WriteStartElement(nameof(IsSelectedAll)); writer.WriteValue(IsSelectedAll); writer.WriteEndElement(); // Сериализуем элементы коллекции XmlSerializer ser = new XmlSerializer(typeof(Chapter)); foreach (var ch in this) ser.Serialize(writer, ch); } }

Дополню ответ вариантом сериализации при композиции.
Наш класс коллекции превращается в такой:
public class ChapterCollection { public bool? IsSelectedAll { get; set; }
public ObservableCollection Collection { get; } = new ObservableCollection(); }
Обратите внимание, создать экземпляр нужно обязательно вручную, сериализатор не может создавать "кастомные" коллекции, поэтому он не заполнит ее: = new ObservableCollection(); Но зато открывать свойство коллекции на запись (set;) не нужно.
Теперь вы можете использовать Linq, но придется указать имя этого свойства-коллекции:
myChapterCollection.Collection.Select(...);
Если вы реализуете IEnumerable<>, то сериализатор опять начнет думать что это коллекция и потеряет свойства, да еще к тому же потребует реализовать нетипизированный метод Add(object), что уже некрасиво и добавляет некоторую хрупкость.
PS: благодаря утиной типизации C# можно все же добавить маленький кусочек сахара, добавив в класс метод:
public IEnumerator GetEnumerator() => ((IEnumerable)Collection).GetEnumerator();
это хоть и не даст воспользоваться LINQ на экземпляре класса, но позволит упростить код foreach
// myChapterCollection.Collection писать не требуется: foreach (var chapter in myChapterCollection) ...

Комментариев нет:

Отправить комментарий