2016-04-01 16 views
1

Ich verwendete XSD.exe, um C# -Objekte basierend auf den XML-Schemas (.xsd-Dateien) automatisch zu generieren. Ich deserialisiere OpenCover-Ausgabe, aber eine der partiellen Klassen wurde nicht korrekt generiert.xsi: type attribute Durcheinander C# XML-Deserialisierung

Hier ist die Linie, die die Ausnahme verursacht:

<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" /> 

Hier ist eine verkürzte Version der MethodPoint Klasse:

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

Jetzt habe ich durch eine Menge von XML-Dateien gegangen, aber Die OpenCover-Ausgabedateien sind die einzigen, die einen Doppelpunkt in einem Attribut enthalten. Das MethodPoint-Objekt ist auch das einzige Objekt, das einen Doppelpunkt in einem Attribut enthält. Wie Sie sehen können, enthält die Klasse nicht das Attribut xsi:type, und ich weiß, dass das einfache Hinzufügen wegen des Doppelpunkts nicht funktioniert. Wie gehen Sie mit dem Präfix xsi um?

ist die rohe XSD aus einer der XML-Dateien OpenCover erzeugt

<?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> 
+1

'xsi: type =" SequencePoint "' bedeutet, dass der tatsächliche Typ dieses Elements ein abgeleiteter Typ von 'MethodPoint' ist, nämlich' SequencePoint'. Siehe [Xsi: type Attribut Binding Support] (https://msdn.microsoft.com/en-us/library/ca1ks327.aspx) und [2.6.1 xsi: type] (https://www.w3.org/ TR/xmlschema-1/# xsi_type). Es sollte eine entsprechende "SequencePoint" -Klasse und ein ['[XmlInclude (typeof (SequencePoint))]] (https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlincludeattribute.aspx) geben) auf 'MethodPoint'. Können Sie Ihre Frage bearbeiten, um die XSD einzuschließen? Hast du es aus dem XML erstellt? – dbc

+0

@dbc Ja, es gibt eine 'SequencePoint' Klasse mit den gleichen Feldern wie die' MethodPoint' Klasse. Ich habe die XSD, die automatisch aus der XML-Datei über XSD.exe als auch generiert wurde. –

+0

Ich würde nicht stören, ein Xsd hinzufügen - ich kann die Ausgabe jederzeit erweitern und dann wird Ihr Code brechen. Da Sie .NET verwenden, laden Sie einfach die OpenCover.Framework-Assembly und verwenden Sie die Typen von dort. –

Antwort

4

Die kurze Antwort ist, dass Sie manuell [XmlInclude(typeof(SequencePoint))] auf Ihre MethodPoint Klasse hinzufügen müssen:

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

Sie müssen auch SequencePoint erben von MethodPoint, wenn es nicht bereits geschieht.

Sie brauchen das, weil tun, wenn Sie xsd.exe verwenden, um einen XSD aus einer XML-Probe zu erzeugen, und dann C# -Klassen es wiederum fügt offenbar nicht polymorphe Subtyp Attribute Typen basieren automatisch, wenn die Attribut xsi:type="SomePolymoirphicSubType" erscheint im XML, obwohl es scheint, dass es sollte.

Die Erklärung ist wie folgt. Das xsi:type-Attribut, kurz für {http://www.w3.org/2001/XMLSchema-instance}type, ist ein w3c standard attribute, das einem Element ermöglicht, seinen Typ explizit zu bestätigen, z. wenn es sich um einen polymorphen Subtyp des erwarteten Elementtyps handelt. XmlSerializersupports this attribute und wird es verwenden, um den tatsächlichen Typ des Objekts zu bestimmen, das für einen solchen polymorphen Typ deserialisiert werden soll. Es erfordert jedoch, im Voraus informiert zu werden aller möglichen Typen mit XmlIncludeAttribute. Wenn ich also die folgende Typenhierarchie erstellen:

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

public class SequencePoint : MethodPoint 
{ 
} 

Und serialisiert es wie folgt:

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

Ich erhalte die folgende XML:

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

Dann, wenn ich es var serializer = new XmlSerializer(typeof(MethodPoint)) mit deserialisieren Ich bekomme eine SequencePoint, nicht seine Basisklasse zurück. Und wenn ich xsd.exe verwenden, um ein Schema für diese Klassen zu generieren, die ich erhalten:

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

Hinweis der xs:extension? So zeigt das XSD einen polymorphen Subtyp an. Und dann, wenn ich xsd ausführen.Exe rückwärts meine Klassen zu regenerieren, die ich erhalten:

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

Wie Sie sehen können, die XmlIncludeAttribute ist und die resultierenden Klassen sind den Originalen äquivalent. Bis jetzt funktioniert alles perfekt.

Es scheint jedoch, dass xsd.exe bei der Ableitung einer XSD aus einer XML-Beispieldatei das Vorhandensein des Attributs xsi:type nicht erkennt. Zum Beispiel, wenn ich ein XSD aus dem trivialen XML oben schaffen, ist das Ergebnis:

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

Die polymorphen Subtyp vollständig fehlt. Von diesem XSD generierte Klassen können dieses XML nicht deserialisieren.

So scheint es, dass das Generieren von C# -Klassen aus einem XML-Beispiel mit xsd.exe nicht so zuverlässig ist wie das Generieren von ihnen aus einem richtigen XSD. Insbesondere in Fällen, in denen xsi:type in der XML-Datei angezeigt wird, müssen Sie entweder die generierten Klassen oder das generierte XSD manuell reparieren, um die erforderliche Hierarchie zu implementieren. Dies kann entweder eine Einschränkung oder ein Fehler im Tool sein.

+0

+1 Sehr hilfreich. Würde wieder upvote. Es sollte beachtet werden, dass Ihre Antwort nur dann gilt, wenn Sie xsd.exe verwendet haben. Ich habe die gleiche Logik für einige Zeit mit Visual Studio "XML als Klassen einfügen" versucht und es würde nicht dauern. Es scheint die gleiche Unfähigkeit zu haben, das xsi: type-Markup zu verstehen, zusammen mit einigen anderen auch. – Sidney

+0

Als jemand, der in den letzten zehn Jahren mit der XML-Serialisierung Schmerzen hatte, ist dies eine der umfassendsten Antworten, die ich je gesehen habe. 10/10, würde wieder serialisieren. – Squiggle

+0

Hey @ dbc, bitte helfen !! Ich stehe vor der gleichen Situation, aber mein Sequenzpunkt liegt in inneren Objekten. Ich habe eine Klasse erben Elternklasse erstellt, aber immer noch nicht das Objekt darin erhalten. Muss ich eine Eigenschaft in method point hinzufügen? Ich habe auch XMlInclude Attribut hinzugefügt –