I used XSD.exe to automatically generate C# objects based on the XML schemas (.xsd files). I'm deserializing OpenCover output, but one of the partial classes didn't get generated correctly.
Here's the line that's causing the exception:
<MethodPoint xsi:type="SequencePoint" vc="0" uspid="1" ordinal="0" offset="0" sl="19" sc="9" el="19" ec="10" bec="0" bev="0" fileid="1" />
Here's a shortened version of the 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)]
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;
}
Now I've been going through a lot of .xml files, but the OpenCover output files are the only ones that contain a colon inside an attribute. The MethodPoint object is also the only object that contains a colon in an attribute. As you can see, the class does not contain the xsi:type
attribute, and I know that simply adding it won't work because of the colon. How do you deal with the xsi
prefix?
Here is the raw .xsd generated from one of the OpenCover XML files
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="CoverageSession" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xs:element name="Summary">
<xs:complexType>
<xs:attribute name="numSequencePoints" type="xs:string" />
<xs:attribute name="visitedSequencePoints" type="xs:string" />
<xs:attribute name="numBranchPoints" type="xs:string" />
<xs:attribute name="visitedBranchPoints" type="xs:string" />
<xs:attribute name="sequenceCoverage" type="xs:string" />
<xs:attribute name="branchCoverage" type="xs:string" />
<xs:attribute name="maxCyclomaticComplexity" type="xs:string" />
<xs:attribute name="minCyclomaticComplexity" type="xs:string" />
<xs:attribute name="visitedClasses" type="xs:string" />
<xs:attribute name="numClasses" type="xs:string" />
<xs:attribute name="visitedMethods" type="xs:string" />
<xs:attribute name="numMethods" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="CoverageSession" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element ref="Summary" />
<xs:element name="Modules">
<xs:complexType>
<xs:sequence>
<xs:element name="Module" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="FullName" type="xs:string" minOccurs="0" msdata:Ordinal="1" />
<xs:element name="ModuleName" type="xs:string" minOccurs="0" msdata:Ordinal="2" />
<xs:element ref="Summary" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="Files" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="File" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="uid" type="xs:string" />
<xs:attribute name="fullPath" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Classes" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Class" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="FullName" type="xs:string" minOccurs="0" />
<xs:element ref="Summary" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="Methods" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="Method" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="MetadataToken" type="xs:string" minOccurs="0" msdata:Ordinal="1" />
<xs:element name="Name" type="xs:string" minOccurs="0" msdata:Ordinal="2" />
<xs:element ref="Summary" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="FileRef" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="uid" type="xs:string" />
</xs:complexType>
</xs:element>
<xs:element name="SequencePoints" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="SequencePoint" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="vc" type="xs:string" />
<xs:attribute name="uspid" type="xs:string" />
<xs:attribute name="ordinal" type="xs:string" />
<xs:attribute name="offset" type="xs:string" />
<xs:attribute name="sl" type="xs:string" />
<xs:attribute name="sc" type="xs:string" />
<xs:attribute name="el" type="xs:string" />
<xs:attribute name="ec" type="xs:string" />
<xs:attribute name="bec" type="xs:string" />
<xs:attribute name="bev" type="xs:string" />
<xs:attribute name="fileid" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="BranchPoints" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="BranchPoint" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="vc" type="xs:string" />
<xs:attribute name="uspid" type="xs:string" />
<xs:attribute name="ordinal" type="xs:string" />
<xs:attribute name="offset" type="xs:string" />
<xs:attribute name="sl" type="xs:string" />
<xs:attribute name="path" type="xs:string" />
<xs:attribute name="offsetend" type="xs:string" />
<xs:attribute name="fileid" type="xs:string" />
<xs:attribute name="offsetchain" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="MethodPoint" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:attribute name="vc" type="xs:string" />
<xs:attribute name="uspid" type="xs:string" />
<xs:attribute name="ordinal" type="xs:string" />
<xs:attribute name="offset" type="xs:string" />
<xs:attribute name="sl" type="xs:string" />
<xs:attribute name="sc" type="xs:string" />
<xs:attribute name="el" type="xs:string" />
<xs:attribute name="ec" type="xs:string" />
<xs:attribute name="bec" type="xs:string" />
<xs:attribute name="bev" type="xs:string" />
<xs:attribute name="fileid" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="visited" type="xs:string" />
<xs:attribute name="cyclomaticComplexity" type="xs:string" />
<xs:attribute name="sequenceCoverage" type="xs:string" />
<xs:attribute name="branchCoverage" type="xs:string" />
<xs:attribute name="isConstructor" type="xs:string" />
<xs:attribute name="isStatic" type="xs:string" />
<xs:attribute name="isGetter" type="xs:string" />
<xs:attribute name="isSetter" type="xs:string" />
<xs:attribute name="skippedDueTo" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="skippedDueTo" type="xs:string" />
<xs:attribute name="hash" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
The short answer is that you need to manually add
[XmlInclude(typeof(SequencePoint))]
to yourMethodPoint
class:You also need to make
SequencePoint
inherit fromMethodPoint
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 usingXmlIncludeAttribute
. Thus, if I create the following type hierarchy:And serialize it as follows:
I get the following XML:
Then if I deserialize it using
var serializer = new XmlSerializer(typeof(MethodPoint))
, I get back aSequencePoint
, not its base class. And if I use xsd.exe to generate a schema for these classes, I get: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: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: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.)