Страницы

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

среда, 22 января 2020 г.

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

#c_sharp #wpf


Есть такой класс:

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

    


Ответы

Ответ 1



Наиболее простым решением является отказ от наследования в пользу композиции. Действительно, логично предположить что 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) ...

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

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