none
XSD - Validierungsfehler bei Attributen mit Default Wert RRS feed

  • Frage

  • Hallo Community,

    ich habe ein Problem bei der Validierung bzw. dem Lesen von XML Attributen mit "default" Werten.
    Das .Net FrameWork verhält sich hier, sagen wir mal komisch. (Getestet mit 4.5 und 4.7.2)

    Folgende Ausgangssituation:
    In einer XSD ist ein Attribut mit Werteliste (strings) angelegt. Zusätzlich gibt es Elemente, welche auf dieses Attribut referenzieren. Die Referenzen werden mit "Default"-Werten belegt. Dies scheint soweit XSD konform zu sein oder ist zumindest ist in der Definition die Verwendung von "ref" und "default" nicht ausgeschlossen.
    ( siehe hier: docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/ms256143(v%3Dvs.100) )

    Die zum Test verwendete XSD sieht folgendermaßen aus:

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:attribute name="class" default="none">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:enumeration value="none" />
            <xs:enumeration value="block" />
            <xs:enumeration value="inline" />
          </xs:restriction>
        </xs:simpleType>
      </xs:attribute>
    
      <xs:element name="container">
        <xs:complexType mixed="true">
          <xs:sequence minOccurs="0" maxOccurs="unbounded">
            <xs:element ref="inline"/>
          </xs:sequence>
          <xs:attribute ref="class" default="block"/>
        </xs:complexType>
      </xs:element>
    
      <xs:element name="inline">
        <xs:complexType mixed="true">
          <xs:attribute ref="class" default="inline"/>
        </xs:complexType>
      </xs:element>
    </xs:schema>

    Das zu prüfende Test-XML ist:

    <?xml version="1.0" encoding="UTF-8"?>
    <container>This is a test <inline>inline</inline> text block</container>


    Zum Testen verwende ich folgende MS-Test Methode:

            [TestMethod]
            public void ValidateDefaultAttributes()
            {
                //load test document
                var xDoc = XDocument.Load("test.xml", LoadOptions.PreserveWhitespace);
    
                //load and compile the schemaSet
                var schemaSet = new XmlSchemaSet();
                schemaSet.Add("", "test.xsd");
                schemaSet.Compile();
    
                //validate the document
                xDoc.Validate(schemaSet, (s, o) => { }, true);
    
                //check the values of class attributes
                Assert.AreEqual(xDoc.Root?.Name, "container");
                Assert.AreEqual(xDoc.Root?.Attribute("class")?.Value, "block");
    
                Assert.AreEqual(xDoc.Root?.Elements().FirstOrDefault()?.Name, "
    inline
    ");
                Assert.AreEqual(xDoc.Root?.Elements().FirstOrDefault()?.Attribute("class")?.Value, "inline");
            }


    Ergebnis:
    Die Asserts zur Prüfung der Attributwerte schlagen fehl, da der Wert der Attribute immer "none" ist.
    XDocument.Validate(...) läd die Informationen zum Attribut aus der Definition und liest auf dort den Default.

    Erwartetes Ergebnis:
    Die Werte der Class-Attribute sollten gemäß der XSD am Container "block" und am Inline "inline" sein.
    d.H. die Defaults sollten dort gelesen werden wo das Attribut referenziert wird. 

    Weiteres Problem - ref auf Attribut ohne Default:
    Fehlt die Definition eines Default-Werts an der Attributs-Definition in der XSD schlägt bereits der Aufruf von XDocument.Validate(...) fehl und liefert eine "System.ArgumentNullException".
    Die Validierung erkennt an der Referenz, dass ein Default gesetzt ist, versucht diesen aber von der Definition zu laden.

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:attribute name="class">
        <xs:simpleType>
          <xs:restriction base="xs:string">
    ...
       bei System.Xml.Linq.XAttribute..ctor(XName name, Object value)
       bei System.Xml.Schema.XNodeValidator.ValidateAttributes(XElement e)
       bei System.Xml.Schema.XNodeValidator.ValidateElement(XElement e)
       bei System.Xml.Schema.XNodeValidator.Validate(XObject source, XmlSchemaObject partialValidationType, Boolean addSchemaInfo)
       bei System.Xml.Schema.Extensions.Validate(XDocument source, XmlSchemaSet schemas, ValidationEventHandler validationEventHandler, Boolean addSchemaInfo)
       bei XsdValidationTest.ValidateDefaultAttributes() in ...\XsdValidationTest.cs.

    Einzige Lösung die ich gefunden habe:
    Wenn das Class-Attribut innerhalb der Elemente neu definiert und nicht referenziert wird, dann sind die Werte korrekt.
    Leider tritt das Problem bei der Verarbeitung von "DITA"-Daten auf. Es ist wahrscheinlich nur schwer durchzusetzen, dass die gesamten DITA-XSDs angepasst werden müssen.

    Ich habe ebenfalls darüber nachgedacht den "XNodeValidator" an dieser Stelle zu überschreiben, allerdings ist dieser eine interne Klasse aus dem System.Xml.Schema Namespace. Es wird dort noch tiefer in den Validator gegriffen, in dem anschließend auf die Definition und nicht die Referenzierung zugegriffen wird. Dies wurde mir daher etwas zu komplex und habe ich den Ansatz verworfen.
    (siehe Z202ff : referencesource.microsoft.com/#System.Xml.Linq/System/Xml/Linq/XNodeValidator.cs,4d6c2db5fc2e9622,references )

    Und nun hoffe ich auf euer Feedback

    Bin ich hier tatsächlich auf einen Fehler im Framework gestoßen oder übersehe ich ein triviales Detail?
    Ich hoffe da draußen in der weiten Welt hat schon einer von euch das Problem gelöst und ich wäre dankbar, wenn ihr eure Erfahrung mit mir teilen würdet.

    Vielen Dank bereits im Voraus,
    M. Fried



    • Bearbeitet M. Fried Dienstag, 13. August 2019 14:13 Typo im Beispielcode
    Montag, 12. August 2019 11:26

Alle Antworten

  • Hallo M. Fried,

    Einzige Lösung die ich gefunden habe:
    Wenn das Class-Attribut innerhalb der Elemente neu definiert und nicht referenziert wird, dann sind die Werte korrekt.

    Würdest Du die gefundene Lösung in einen separaten Beitrag ausgliedern, damit diese als Antwort gekennzeichnet werden kann (wenn keine bessere bekannt gegeben wird)? Dadurch wird die markierte Antwort hervorgehoben und für künftige Leser dieses Threads, die das gleiche Anliegen haben, übersichtlicher.

    Weiteres Problem - ref auf Attribut ohne Default:
    Fehlt die Definition eines Default-Werts an der Attributs-Definition in der XSD schlägt bereits der Aufruf von XDocument.Validate(...) fehl und liefert eine "System.ArgumentNullException".
     Die Validierung erkennt an der Referenz, dass ein Default gesetzt ist, versucht diesen aber von der Definition zu laden.

    Wird die Ausnahme ausgelöst, wenn Du das type-Attribut angibst? In folgendem Thread findest Du ein Beispiel dazu sowie eine Problemumgehung im Codebehind:
    Error while validating XML

    Gruß,
    Dimitar


    Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip „IT-Pros helfen IT-Pros“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Dienstag, 13. August 2019 10:49
    Moderator
  • Würdest Du die gefundene Lösung in einen separaten Beitrag ausgliedern, damit diese als Antwort gekennzeichnet werden kann (wenn keine bessere bekannt gegeben wird)? Dadurch wird die markierte Antwort hervorgehoben und für künftige Leser dieses Threads, die das gleiche Anliegen haben, übersichtlicher.

    Einzige Lösung die ich gefunden habe:

    Wenn das Class-Attribut innerhalb der Elemente neu definiert und nicht referenziert wird, dann sind die Werte korrekt. Allerdings muss hierzu die gesamte XSD umstrukturiert werden und die Attributs-Definition vervielfacht sich. Daher löst dieses Vorgehen zwar das Problem, verschlechtert aber die Wartbarkeit der XSD.

    Beispiel:

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:element name="container">
        <xs:complexType mixed="true">
          <xs:sequence minOccurs="0" maxOccurs="unbounded">
            <xs:element ref="inline"/>
          </xs:sequence>
          <!-- xs:attribute ref="class" default="block"/ -->
          <xs:attribute name="class" default="block">
            <xs:simpleType>
              <xs:restriction base="xs:string">
                <xs:enumeration value="none" />
                <xs:enumeration value="block" />
                <xs:enumeration value="inline" />
              </xs:restriction>
            </xs:simpleType>
          </xs:attribute>
        </xs:complexType>
      </xs:element>
    
      <xs:element name="inline">
        <xs:complexType mixed="true">
          <!-- xs:attribute ref="class" default="inline"/ -->
          <xs:attribute name="class" default="inline">
            <xs:simpleType>
              <xs:restriction base="xs:string">
                <xs:enumeration value="none" />
                <xs:enumeration value="block" />
                <xs:enumeration value="inline" />
              </xs:restriction>
            </xs:simpleType>
          </xs:attribute>
        </xs:complexType>
      </xs:element>
    </xs:schema>

    Grüße,
    M. Fried

    Dienstag, 13. August 2019 13:52
  • Wird die Ausnahme ausgelöst, wenn Du das type-Attribut angibst? In folgendem Thread findest Du ein Beispiel dazu sowie eine Problemumgehung im Codebehind:
    Error while validating XML

    Hallo Dimitar,

    ich möchte noch kurz auf den von dir angesprochenen Thread eingehen.
    Leider werden in dieser Lösung vor der Validierung alle Default und FixValues gelöscht, sodass diese natürlich nicht mehr zu einem Fehler führen können.
    Jedoch geht damit auch die Funktionalität verloren, dass bei der Validierung die Default-Werte in das XML eingetragen werden. Somit ist dies leider keine Lösung.

    Teillösung die funktioniert, wenn die XSD überarbeitet werden kann:
    Wie von dir angesprochen kann ein definierter Type anstelle des definieren Attributs verwendet werden. Dadurch tritt das Problem nicht mehr auf und es hat nur minimale Auswirkungen auf die Wartbarkeit der XSD.

    Beispiel XSD:

    <?xml version="1.0" encoding="UTF-8"?>
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <!--<xs:attribute name="class">-->
        <xs:simpleType name="classType">
          <xs:restriction base="xs:string">
            <xs:enumeration value="none" />
            <xs:enumeration value="block" />
            <xs:enumeration value="inline" />
          </xs:restriction>
        </xs:simpleType>
      <!--</xs:attribute>-->
    
      <xs:element name="container">
        <xs:complexType mixed="true">
          <xs:sequence minOccurs="0" maxOccurs="unbounded">
            <xs:element ref="inline"/>
          </xs:sequence>
          <!--<xs:attribute ref="class" default="block"/>-->
          <xs:attribute name="class" type="classType" default="block"/>
        </xs:complexType>
      </xs:element>
    
      <xs:element name="inline">
        <xs:complexType mixed="true">
          <!--<xs:attribute ref="class" default="inline"/>-->
          <xs:attribute name="class" type="classType" default="inline"/>
        </xs:complexType>
      </xs:element>
    </xs:schema>

    Mein Problem wird dadurch leider noch nicht behoben, da ich als Grundlage die DITA XSD Struktur habe.
    Den Standard DITA kann ich leider nicht anpassen ;-(
    Werde als nächstes mal versuchen ob die XSD im Speicher nach entsprechenden Problemfällen durchsucht werden kann und ob diese eventuell mit ein paar Zeilen Code zu beheben sind.

    Danke und viele Grüße,
    M. Fried

    Dienstag, 13. August 2019 14:09