views:

59

answers:

1

I have a List which is populated with objects of various concrete types which subclass BaseType

I am using the WCF DataContractSerializer

<Children>
    <BaseType xmlns:d3p1="http://schemas.datacontract.org/2004/07/Tasks"
              i:type="d3p1:ConcreteTypeA"></BaseType>
    <BaseType xmlns:d3p1="http://schemas.datacontract.org/2004/07/Tasks"
              i:type="d3p1:ConcreteTypeB"></BaseType>
</Children>

Is there any way to get this to generate

<Children>
    <ConcreteTypeA/>
    <ConcreteTypeB/>
</Children>

?

The real goal is to let users generate some XML to load into memory, and the users are of a skill level that asking them for the original XML is not going to be successful.

+4  A: 

DataContractSerializer is not designed to let you control the output. It's designed to be fast, implicit, and easy to attribute a class with.

What you want is the XmlSerializer. This gives you a lot more control over the XML output.

Note that in my example below I specified a lot of things that could have been inferred from the property names but just to give you the sense that you can override them in the attributes. In fact I think this whole class would serialize just fine if all the attributes were removed and some KnownTypeAttributes were applied but I haven't tested it. I don't know if this will give you the exact XML you described (it will create a root element above Children) but hopefully this sets you in the right direction.

Attributes That Control XML Serialization

[XmlRoot(Namespace="")]
public class MyClass {

    [XmlArray("Children")]
    [XmlArrayItem("ConcreteTypeA", typeof(ConcreteTypeA))]
    [XmlArrayItem("ConcreteTypeB", typeof(ConcreteTypeB))]
    public BaseType[] Children {
        get;
        set;
    }

}

public class BaseType {
}

public class ConcreteTypeA : BaseType {
}

public class ConcreteTypeB : BaseType {
}

EDIT: I just tested and it produces something very close to what you were seeking.

void Main()
{

    var mc = new MyClass();
    mc.Children = new BaseType[] {
        new ConcreteTypeA(),
        new ConcreteTypeB(),
        new ConcreteTypeA(),
        new ConcreteTypeB()
    };

    var serializer = new XmlSerializer(typeof(MyClass));

    using ( var str = new StringWriter() ) {
        serializer.Serialize(str, mc);
        str.ToString().Dump();
    }

}

...produces... (useless xmlns removed from the top)

<MyClass>
  <Children>
    <ConcreteTypeA />
    <ConcreteTypeB />
    <ConcreteTypeA />
    <ConcreteTypeB />
  </Children>
</MyClass>
Josh Einstein
Can I add known types at runtime rather than via attributing? Types may be added in by the user via a plugin model.
Jason Coyne
Yes there is an overload of the XmlSerializer's constructor that allows you to pass in the additional types to consider in a type array. http://msdn.microsoft.com/en-us/library/e5aakyae(v=VS.90).aspx
Josh Einstein