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

332
04 июля 2017, 20:03

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

public class ChapterCollection : ObservableCollection<ChapterVM>
{
    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);
    }
}
Answer 1

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

Предпочитайте композицию наследованию

Если всё же вы намерены оставить наследование, для переопределения действий сериализатора необходимо реализовать интерфейс IXmlSerializable. Сделать это можно примерно так:

public class ChapterCollection : ObservableCollection<Chapter>, 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<Chapter> Collection { get; } = new ObservableCollection<Chapter>();
}

Обратите внимание, создать экземпляр нужно обязательно вручную, сериализатор не может создавать "кастомные" коллекции, поэтому он не заполнит ее: = new ObservableCollection<Chapter>();
Но зато открывать свойство коллекции на запись (set;) не нужно.

Теперь вы можете использовать Linq, но придется указать имя этого свойства-коллекции:

myChapterCollection.Collection.Select(...);

Если вы реализуете IEnumerable<>, то сериализатор опять начнет думать что это коллекция и потеряет свойства, да еще к тому же потребует реализовать нетипизированный метод Add(object), что уже некрасиво и добавляет некоторую хрупкость.

PS: благодаря утиной типизации C# можно все же добавить маленький кусочек сахара, добавив в класс метод:

public IEnumerator<Chapter> GetEnumerator()
    => ((IEnumerable<Chapter>)Collection).GetEnumerator();

это хоть и не даст воспользоваться Linq на экземпляре класса, но позволит упростить код foreach:

// myChapterCollection.Collection писать не требуется:
foreach (var chapter in myChapterCollection)
    ...
READ ALSO
Работа со спрайтами

Работа со спрайтами

Как взять Sprite из Spritesheet через ResourcesLoad(path); язык c#

244
Telegram Bot State Machine

Telegram Bot State Machine

Добрый день, необходимо реализовать телеграм бота c# на паттерне Конечный Автомат (Машина состояний), сталкивался ли кто либо с подобной задачей?...

1006
Quartz.Net IoC Unity DI

Quartz.Net IoC Unity DI

Есть класс IJob

509