4

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>
dbc
  • 104,963
  • 20
  • 228
  • 340
Kieran Paddock
  • 375
  • 1
  • 5
  • 15
  • 1
    `xsi:type="SequencePoint"` means that the actual type of this element is a derived type of `MethodPoint`, namely `SequencePoint`. See [Xsi:type Attribute Binding Support](https://msdn.microsoft.com/en-us/library/ca1ks327.aspx) and [2.6.1 xsi:type](https://www.w3.org/TR/xmlschema-1/#xsi_type). There should be a corresponding `SequencePoint` class and an [`[XmlInclude(typeof(SequencePoint))]`](https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlincludeattribute.aspx) on `MethodPoint`. Can you edit your question to include the XSD? Did you create it from the XML? – dbc Apr 01 '16 at 18:28
  • @dbc Yep, there is a `SequencePoint` class with the same fields as the `MethodPoint` class. I included the XSD, which was auto-generated from the XML through XSD.exe as well. – Kieran Paddock Apr 01 '16 at 18:40
  • I wouldn't bother adding an xsd - I may extend the ouput at any time and then your code will break. Since you are using.NET why not just load up the OpenCover.Framework assembly and use the types from there? – Shaun Wilde Apr 02 '16 at 23:37

1 Answers1

12

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.)

dbc
  • 104,963
  • 20
  • 228
  • 340
  • +1 Very helpful. Would upvote again. It should be noted that your answer only applies if you used xsd.exe I tried the same logic for quite some time using visual studio's "Paste XML as classes" and it wouldn't take. It appears to have the same inability to understand the xsi:type markup, along with a few others as well. – Sidney Dec 30 '16 at 15:50
  • As someone who has experienced pain with XML serialization for the past decade, this is one of the most comprehensive answers I've ever seen. 10/10, would serialize again. – Squiggle Dec 30 '16 at 15:50
  • Hey @dbc , Please help!! I am facing the same situation but my sequencepoint lies in inner objects. I have created a class inheriting parent class but still not getting the object in it. Do i need to add a property in method point ?? I have also added XMlInclude attribute – Rohit Arora Feb 02 '18 at 07:32
  • I created this via paste XML as classes – Rohit Arora Feb 02 '18 at 07:42
  • @RohitArora - It's been a year and a half since I answered this. I found my test code, which is here: https://dotnetfiddle.net/H0XsML. If your case is different I'd suggest asking a new question and linking back to this one. This question never had a complete [mcve] so I tested a simplified example. If you do ask a related question I'd recommend including a complete example. – dbc Feb 02 '18 at 07:43
  • *I created this via paste XML as classes* -- right, and the point of this answer is that **Paste XML as Classes** (which uses `xsd.exe` internally) does not work correctly in the presence of `xsi:type` attributes in the XML. In such cases manual fixing of the generated c# code will be required. – dbc Feb 02 '18 at 07:44
  • Yes, I am trying to manually fix it but still getting different errors. Will create a new question for this and will give you the link too. Thanks!! – Rohit Arora Feb 02 '18 at 07:48
  • 1
    @RohitArora - best way to fix it would be to find the appropriate XSD schema file(s) and use them instead. The problem is only symptomatic when generating classes from XML rather than XSD. – dbc Feb 02 '18 at 07:50
  • @dbc Thanks!! I did not know before about XSD schemas. That were also provided to me. That works perfect now!!! Thanks a lot – Rohit Arora Mar 23 '18 at 06:23
  • Re-edited comment: I know this works fine without any error but, I have a question, What is the necessity of SequencePoint class here, when all it does is returns nothing. Also, what happens if we have multiple type names like SequencePointType. Do we need to add those as class names always? – Marshall Jan 24 '19 at 17:27
  • @Marshall - *What is the necessity of SequencePoint class here, when all it does is returns nothing.* - it's needed because the system that created the XML created and serialized this class hierarchy, and thus the receiving system needs an equivalent hierarchy in order to deserialize the XML. There's not enough information in the question to know why the original system is designed that way. – dbc Jan 24 '19 at 21:15
  • @Marshall - *Do we need to add those as class names always?* Generally yes. See [Using XmlSerializer to serialize derived classes](https://stackoverflow.com/a/1643424/3744182) and/or [How to add XmlInclude attribute dynamically](https://stackoverflow.com/a/2689660). – dbc Jan 24 '19 at 21:16
  • Thanks, for the response. I will take a look at those links. – Marshall Jan 28 '19 at 16:43