C # Xml-сериализация абстрактного базового типа в производный тип - какое определение свойства TypeName лучше?

Мой сценарий:

У меня есть объект, который я определил со свойствами, которые украшены тегами XmlElement и имеют типы, которые я определил, некоторые из которых набраны как абстрактные, для которых устанавливаются соответствующие производные типы. Я хочу сериализовать весь этот объект в XML с помощью XmlSerializer, и все абстрактные свойства должны сериализоваться как элементы с TypeName, для которого задано TypeName производного типа.

Это пример того, как структурированы объекты:

[XmlType(TypeName = "MAINOBJECT")]
public class MainObject
{
    [XmlElement(Type = typeof(DerivedClass))]
    public BaseClass TheBase { get; set; }
}

[XmlInclude(typeof(DerivedClass))]
public abstract class BaseClass
{
    [XmlAttribute("AnAttribute")]
    public string AnAttribute { get; set; }

    [XmlElement("ANELEMENT")]
    public string AnElement { get; set; }

}

[XmlType(TypeName = "DERIVEDCLASS")]
public class DerivedClass : BaseClass
{
    [XmlElement("ANOTHERELEMENT")]
    public string AnotherElement { get; set; }

}

Однако обратите внимание, что когда я создаю новый экземпляр MainObject, заполняю его свойства и сериализую его, сгенерированный XML выглядит следующим образом:

<MAINOBJECT>
    <BaseClass AnAttribute="">
            <ANELEMENT/>
            <ANOTHERELEMENT/>
    </BaseClass>
</MAINOBJECT>

Я хочу вот что:

<MAINOBJECT>
    <DERIVEDCLASS AnAttribute="">
            <ANELEMENT/>
            <ANOTHERELEMENT/>
    </DERIVEDCLASS>
</MAINOBJECT>

Есть какие-нибудь подсказки, что я здесь делаю не так?


person null    schedule 03.09.2014    source источник


Ответы (3)


Добавьте имя XmlElement в TheBase в MainObject следующим образом:

[XmlType(TypeName = "MAINOBJECT")]
public class MainObject
{
    [XmlElement("DERIVEDCLASS", Type = typeof(DerivedClass))]
    public BaseClass TheBase { get; set; }
}
person Wagner DosAnjos    schedule 03.09.2014
comment
Поймите, я не хочу жестко кодировать имя элемента, я хочу, чтобы он динамически выбирал любой класс, производный от BaseClass, который я назначаю для TheBase, который использует в качестве своего TypeName. - person null; 03.09.2014
comment
Это не то, как работает сериализация, потому что, если есть несколько свойств одного типа, их невозможно будет отличить друг от друга. Например, public BaseClass TheBase и public BaseClass TheBase2. - person Wagner DosAnjos; 04.09.2014
comment
В этом случае первым будет по умолчанию TheBase, а вторым - TheBase2, если вы не указали TypeName. однако у меня есть имя типа, указанное для производного типа, который я назначаю TheBase. Я хочу, чтобы он использовал TypeName производного типа при сериализации. Я неправильно об этом думаю? - person null; 04.09.2014
comment
Да, это похоже на свойство string, которому присвоено имя string. Например, если у вас было public string TheBase, ожидаете ли вы, что сериализованное имя будет <string> или <TheBase>? - person Wagner DosAnjos; 04.09.2014
comment
чтобы ответить на это, если бы TheBase реализовала строку, а я имел дело со строкой, как TheBase, которая буквально ЯВЛЯЕТСЯ строкой, я бы ожидал, что сериализованное имя будет ‹TheBase›, а не ‹string›. - person null; 04.09.2014
comment
Точно так же и с public BaseClass TheBase. TheBase будет сериализован как ‹TheBase›. - person Wagner DosAnjos; 04.09.2014
comment
Что ж, это работает, смеется. Я должен был хотя бы попробовать, прежде чем отклонить его сначала, большое вам спасибо :) И под этим я имею в виду [XmlElement (DERIVEDCLASS, Type = typeof (DerivedClass))] public BaseClass TheBase {get; набор; } - person null; 04.09.2014

Мне кажется, что лучшим решением здесь было бы реализовать IXmlSerializable, чтобы вы могли полностью контролировать сериализацию объектов. Конечно, это больше работы, но если у вас есть такие требования, которые несколько необычны, вы также можете столкнуться с дополнительными причудами, когда стандартный XmlSerializer не будет работать для вас в будущем.

Здесь есть хороший учебник: Как правильно реализовать IXmlSerializable

Кроме того, здесь есть полезная информация: Как правильно реализовать IXmlSerializable?

person Wyatt Earp    schedule 04.09.2014
comment
Теперь я собираюсь исследовать использование IXmlSerializable, я отмечу это как правильный ответ, если я смогу заставить свою реализацию работать. Спасибо! - person null; 04.09.2014

Может быть, не лучшее решение, но:

class Program
    {
        static void Main(string[] args)
        {
            XmlAttributes attrs = new XmlAttributes();

            XmlElementAttribute attr = new XmlElementAttribute();
            attr.ElementName = "DerivedClass";
            attr.Type = typeof(DerivedClass);

            attrs.XmlElements.Add(attr);

            XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();

            attrOverrides.Add(typeof(MainObject), "TheBase", attrs);

            XmlSerializer s = new XmlSerializer(typeof(MainObject), attrOverrides);

            StringWriter writer = new StringWriter();

            MainObject mo = new MainObject { TheBase = new DerivedClass { AnAttribute = "AnAttribute", AnElement = "AnElement", AnotherElement = "AotherElement" } };

            s.Serialize(writer, mo);
            Console.Write(writer.ToString());
        }
    }
person Greenonion    schedule 03.09.2014