xsi:type attribute messing up C# XML deserialization

The short answer is that you need to manually add [XmlInclude(typeof(SequencePoint))] to your MethodPoint class:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[XmlInclude(typeof(SequencePoint))]
public partial class CoverageSessionModulesModuleClassesClassMethodsMethodMethodPoint {
    private string vcField;
    private string uspidField;
    private string ordinalField;
    private string offsetField;
    private string slField;
    private string scField;
    private string elField;
    private string ecField;
    private string becField;
    private string bevField;
    private string fileidField;
}

You also need to make SequencePoint inherit from MethodPoint if it does not already do so.

You need to do this because, when you use xsd.exe to generate an XSD from an XML sample, and then c# classes in turn, it apparently doesn’t add polymorphic subtype attributes to base types automatically when the attribute xsi:type="SomePolymoirphicSubType" appears in the XML, even though it seems it should.

The explanation is as follows. The xsi:type attribute, short for {http://www.w3.org/2001/XMLSchema-instance}type, is a w3c standard attribute that allows an element to explicitly assert its type, e.g. when it is a polymorphic subtype of the expected element type. XmlSerializer supports this attribute and will use it to determine the actual type of object to deserialize for such a polymorphic type. However, it requires to be informed in advance of all possible types using XmlIncludeAttribute. Thus, if I create the following type hierarchy:

[XmlInclude(typeof(SequencePoint))]
public class MethodPoint
{
}

public class SequencePoint : MethodPoint
{
}

And serialize it as follows:

var test = new SequencePoint();

var serializer = new XmlSerializer(typeof(MethodPoint));
var sb = new StringBuilder();
using (var stream = new StringWriter(sb))
    serializer.Serialize(stream, test);

Console.WriteLine(sb);

I get the following XML:

<MethodPoint 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
 xsi:type="SequencePoint" />

Then if I deserialize it using var serializer = new XmlSerializer(typeof(MethodPoint)), I get back a SequencePoint, not its base class. And if I use xsd.exe to generate a schema for these classes, I get:

<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="MethodPoint" nillable="true" type="MethodPoint" />
  <xs:complexType name="MethodPoint" />
  <xs:complexType name="SequencePoint">
    <xs:complexContent mixed="false">
      <xs:extension base="MethodPoint" />
    </xs:complexContent>
  </xs:complexType>
  <xs:element name="SequencePoint" nillable="true" type="SequencePoint" />
</xs:schema>

Notice the xs:extension? That’s how the XSD indicates a polymorphic subtype. And then if I run xsd.exe backwards to regenerate my classes, I get:

[System.Xml.Serialization.XmlIncludeAttribute(typeof(SequencePoint))]
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=true)]
public partial class MethodPoint {
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.3038")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=true)]
public partial class SequencePoint : MethodPoint {
}

As you can see, the XmlIncludeAttribute is there and the resulting classes are equivalent to the originals. Everything is working perfectly so far.

But, it seems that when inferring an XSD from a sample XML file, xsd.exe doesn’t pick up on the presence of the xsi:type attribute. For instance, if I create an XSD from the trivial XML above, the result is:

<xs:schema id="MethodPoint" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="MethodPoint" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
    <xs:complexType>
      <xs:choice minOccurs="0" maxOccurs="unbounded" />
    </xs:complexType>
  </xs:element>
</xs:schema>

The polymorphic subtype is completely missing. Classes generated from this XSD will not be able to deserialize that XML.

So it seems that generating c# classes from an XML sample with xsd.exe just isn’t as reliable as generating them from a proper XSD. Specifically, in cases where xsi:type appears in the XML file, you will need to manually fix either the generated classes or the generated XSD to implement the required hierarchy. This may be either a limitation or a bug in the tool.

(The limitation/bug will also appear in Paste XML as Classes which uses xsd.exe internally.)

Leave a Comment