Сериализация: паттерн composite

281
05 апреля 2018, 11:50

Как нужно записывать в xml-файл объекты класса? Я сделал это так, но при добавлении листа (Leaf) появляется исключение:

InvalidOperationException: Тип TreeS.Leaf не ожидался. Используйте атрибут XmlInclude или SoapInclude для задания типов, которые не известны как статические.

Что нужно исправить?

using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Collections.Generic;
namespace TreeS
{
    public abstract class Question
    {
        [XmlElement("Question")]
        public string name;
        public Question()
        {
        }
        public Question(string name)
        {
            this.name = name;
        }
        public abstract void Display();
        public abstract void Add(Question c);
        public abstract void Remove(Question c);
        public virtual void Print()
        {
            Console.WriteLine(name);
        }
    }
    public class Answer : Question
    {
        [XmlArray("Question1"), XmlArrayItem("Leaf")]
        private List<Question> children = new List<Question>();
        public List<Question> GetChildren { get => children; }
        public Answer()
        {
        }
        public Answer(string name)
            : base(name)
        {
        }
        public override void Add(Question component)
        {
            children.Add(component);
        }
        public override void Remove(Question component)
        {
            children.Remove(component);
        }
        public override void Display()
        {
            Console.WriteLine(name);
            foreach (Question component in children)
            {
                component.Display();
            }
        }
        public override void Print()
        {
            Console.WriteLine("Вопрос: " + name);
            if (children.Count != 0)
            {
                Console.WriteLine("Ответы:");
                for (int i = 0; i < children.Count; i++)
                {
                    children[i].Print();
                }
                Console.WriteLine('\n');
            }
        }
    }
    public class Leaf : Question
    {
        public Leaf()
        {
        }
        public Leaf(string name)
            : base(name)
        {
        }
        public override void Display()
        {
            Console.WriteLine(name);
        }
        public override void Add(Question component)
        {
            throw new NotImplementedException();
        }
        public override void Remove(Question component)
        {
            throw new NotImplementedException();
        }
    }
    class Program
    {
        public static void Main()
        {
            Question question = new Answer("123");
            Question question1 = new Answer("5");
            Question answer = new Leaf("qwe");
            Question answer1 = new Answer("asd");
            question.Add(question1);
            question.Add(answer);
            question.Add(answer1);
            question1.Add(answer);
            question.Print();
            CreateXML("tree.xml", question);
            Answer question2 = GetXML("tree.xml");
            Console.ReadKey();
        }
        private static void CreateXML(string filename, Question question)
        {
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(Answer));
            StringWriter stringWriter = new StringWriter();
            TextWriter writer = new StreamWriter(filename);
            xmlSerializer.Serialize(writer, question);
            xmlSerializer.Serialize(stringWriter, question);
            writer.Close();
        }
        private static Answer GetXML(string filename)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Answer));
            FileStream fs = new FileStream(filename, FileMode.Open);
            Answer collection;
            collection = (Answer)serializer.Deserialize(fs);
            return collection;
        }
    }
}

Делал по примеру Компоновщик

Answer 1

Чтобы (де)сериализация заработала, нужно сделать то, что сказано в описании ошибки. Добавляем атрибуты XmlInclude:

[XmlInclude(typeof(Answer))]
[XmlInclude(typeof(Leaf))]
public abstract class Question

Благодаря им сериализатор знает, какие классы-потомки могут оказаться вместо базового класса.

Оставим в стороне иерархию ваших классов (с какой стати ответ наследует от вопроса?), поговорим о коде сериализации.

В метод CreateXML передаётся тип Question, а сериализатор создаётся для типа Answer. Но ведь там может оказаться и Leaf.

Из метода GetXML возвращается тип Answer - это явно подогнано под тип сериализатора в предыдущем методе. Явные нарушения логики работы.

Не меняя логику ваших методов, просто перепишу их, чтобы сделать более устойчивыми к возможным исключениям.

private static void CreateXML(string filename, Question question)
{
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(Answer));
    using (var writer = new StreamWriter(filename))
    {
        xmlSerializer.Serialize(writer, question);
    }
}
private static Answer GetXML(string filename)
{
    XmlSerializer serializer = new XmlSerializer(typeof(Answer));
    using (FileStream fs = new FileStream(filename, FileMode.Open))
    {
        Answer answer = (Answer)serializer.Deserialize(fs);
        return answer;
    }
}

У вас зачем-то делается сериализация в StringWriter.
И почему имя локальной переменной collection?

READ ALSO
WPF и ASP.NET видео уроки на русском на C#

WPF и ASP.NET видео уроки на русском на C#

Посоветуйте хорошие видео уроки по WPF и ASPNET (С#) на русском

242
Paint on C# сохранение + рисование

Paint on C# сохранение + рисование

Привет всемХотел узнать а как можно реализовать сохранение картинки + то что я на ней нарисовал, сама функция для сохранения реализована,...

229
Интеграционное тестирование .Net приложений

Интеграционное тестирование .Net приложений

Здравствуйте, мне необходимо реализовать интеграционные тесты настольногоNet приложения, которые будут запускаться после каждого билда

258
WPF. Часть текста Title изменить шрифт на Bold

WPF. Часть текста Title изменить шрифт на Bold

У формы есть заголовок окна TitleМне нужно назвать приложение например: Приложение организации Тест, слово тест выделить жирным

203