1

My scenario:

I have an object that I have defined with properties that are decorated with XmlElement tags and that have types that I have defined, some of which are typed as abstract that get set to respective derived types. I want to serialize this entire object into XML using XmlSerializer, and all properties that are abstract should get serialized as elements with TypeName set to the TypeName of the derived type.

This is an example of how the objects are structured:

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

}

Note, however, that when I create a new instance of MainObject, populate it's properties and serialize it, this is what the generated XML looks like:

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

What I want is this:

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

Any clue what I'm doing wrong here?

null
  • 153
  • 1
  • 3
  • 16

3 Answers3

1

Add the XmlElement name to TheBase in MainObject as follows:

[XmlType(TypeName = "MAINOBJECT")]
public class MainObject
{
    [XmlElement("DERIVEDCLASS", Type = typeof(DerivedClass))]
    public BaseClass TheBase { get; set; }
}
Wagner DosAnjos
  • 6,304
  • 1
  • 15
  • 29
  • Understand that I don't want to hard code the name of the element, I want it to dynamically select whatever class derived from BaseClass I assign to TheBase uses as its TypeName. – null Sep 03 '14 at 19:44
  • That's not how serialization works, because if there are multiple properties of the same type there would be no way to tell them apart. For example, `public BaseClass TheBase` and `public BaseClass TheBase2`. – Wagner DosAnjos Sep 03 '14 at 20:05
  • In that case, the first one would default to be "TheBase" and the second would be "TheBase2" unless you specified the TypeName. however, I have a type name specified for the derived type that I am assigning to TheBase. I want it to use the derived type's TypeName when serialized. Am I thinking about this wrong? – null Sep 03 '14 at 20:45
  • Yes, it is akin of a `string` property be given the name `string`. For example, if you had `public string TheBase`, do you expect the serialized name to be `` or ``? – Wagner DosAnjos Sep 04 '14 at 12:08
  • to respond to this, if TheBase implemented string, and I was dealing with a string, as a TheBase, which literally IS a string, I'd expect the serialized name to be the and not . – null Sep 04 '14 at 17:00
  • Exactly, that's the same case with `public BaseClass TheBase`. TheBase will be serialized as . – Wagner DosAnjos Sep 04 '14 at 17:04
  • 1
    Well, this works lol. I should have at least tried it before dismissing it initially, thank you so much :) And by "this", I mean [XmlElement("DERIVEDCLASS", Type = typeof(DerivedClass))] public BaseClass TheBase { get; set; } – null Sep 04 '14 at 17:46
1

It seems to me that the best solution here would be to implement the IXmlSerializable interface so that you can have complete control over how the objects get serialized. Sure, it's more work, but if you have requirements like this that are somewhat out of the ordinary, then you may also run into more quirks where the standard XmlSerializer won't work for you down the road.

There is a good tutorial here: How to Implement IXmlSerializable Correctly

Also, there is some good information here: Proper way to implement IXmlSerializable?

Community
  • 1
  • 1
Wyatt Earp
  • 1,783
  • 13
  • 23
  • I am now going to investigate using IXmlSerializable, I'll mark this as the correct answer if I can get my implementation working. Thanks! – null Sep 04 '14 at 13:32
0

Maybe not the best solution, but:

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());
        }
    }
Greenonion
  • 87
  • 5