XML-Validierung: Wozu?

XML-Validierung: Wozu?

XML-Validierung: Wozu?

➪ Die enorme Gestaltungsflexibilität der XML-Dokumente zwingt zu systematischer Strukturierung, damit sie durch Folgeprogramme effizient ausgewertet werden können.

Auf dieser Seite:

Zahlreiche XML-basierte Standards setzen eine bestimmte Struktur voraus: XSL, XML-Schema, SVG, FO, MathML, ANT, Maven, Docbook, DITA, ebenso etablierte Konzepte zur Auswertung von XML (SAX, DOM, ...).

Insofern ist XML eine Schnittstelle zur Datenübergabe an Folgeprogramme, die zum korrekten Funktionieren einen bestimmten Aufbau der XML-Dokumente zwingend einfordern.

Ein simples Beispiel soll das demonstrieren. Um -Stylesheets wie dieses ...


 <xsl:template match="/">
  <Root>   
   <xsl:for-each 
        select="/Orte/Ort/Mensch
          [Gehalt &gt;= sum(Kauf/Gesamt)]">
    <Person>
     <VN>
       <xsl:value-of select="vorname"/>
     </VN>
     <NN>
       <xsl:value-of select="name"/>
     </NN>
     <WO>
       <xsl:value-of select="../name"/>
     </WO>
     <Saldo>
       <xsl:value-of 
            select="Gehalt - sum(Kauf/Gesamt)"/>
     </Saldo>
    </Person>
   </xsl:for-each>   
  </Root>
 </xsl:template>

... oder alternativ um XQuery-Anweisungen wie die folgende:


<Root>
 {
  for $x in /Orte/Ort/Mensch
            [Gehalt >= sum(Kauf/Gesamt)]
  return
   <Person>
    <VN>{$x/vorname/text()}</VN>
    <NN>{$x/name/text()}</NN>
    <WO>{$x/../name/text()}</WO>
    <Saldo>
      {$x/xs:decimal(Gehalt) - 
       sum($x/Kauf/xs:decimal(Gesamt))}
    </Saldo>
   </Person>
 }
</Root>

... oder andere Auswertungsprogramme in diversen Sprachen und APIs zuverlässig schreiben zu können, ist es unerlässlich, über den Aufbau des XML-Dokuments eine klare Vorstellung zu haben. Denn das XPath-Statement


/Orte/Ort/Mensch[Gehalt >= sum(Kauf/Gesamt)]

liefert nur dann brauchbare Ergebnisse, wenn es die Inhalte des XML-Dokuments eindeutig adressiert.

Ein -Statement, das nicht zum Aufbau des XML-Dokuments passt, liefert keine Ergebnisse. Bereits die abweichende Adressierung /Orte/ort/Mensch würde kein XML-Element ansprechen, weil zwischen Ort (im XML-Input) und ort (im XPath-Statement) ein gravierender Unterschied besteht.

Ein geeignetes XML-Dokument könnte beispielsweise diesen Inhalt aufweisen:


<Orte>
  <Ort>
    <id>1</id>
    <name>Neustadt</name>
    <Mensch>
      <id>1</id>
      <name>Holzflos</name>
      <vorname>Hugo</vorname>
      <Gehalt>234.56</Gehalt>
      <idOrt>1</idOrt>
      <Kauf>
        <idMensch>1</idMensch>
        <anzahl>3</anzahl>
        <bez>Hemd</bez>
        <preis>12.99</preis>
        <Gesamt>38.97</Gesamt>
      </Kauf>
      <Kauf>
        <idMensch>1</idMensch>
        <anzahl>9</anzahl>
        <bez>Hemd</bez>
        <preis>12.99</preis>
        <Gesamt>116.91</Gesamt>
      </Kauf>
    </Mensch>
  </Ort>
</Orte>

Daher ist eine Prüfung sinnvoll, ob der XML-Input jenen Annahmen und Voraussetzungen entspricht, denen die Programme zugrunde liegen. Diese Validierung kann vor Beginn oder des Transformationsprozesses erfolgen. Geschieht das nicht, so funktionieren die Folgeprogramme möglicherweise nicht korrekt, und es droht Informationsverlust.

pic/XSD_Orte_Ort_Mensch.jpg


<xs:schema id="Orte" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Orte">
    <xs:complexType>
      <xs:sequence>
        <xs:element 
            ref="Ort" 
            maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="Ort">
    <xs:complexType>
      <xs:sequence>
        <xs:element 
            ref="name"/>
        <xs:element 
            ref="Mensch" 
            maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="Mensch">
    <xs:complexType>
      <xs:sequence>
        <xs:element 
            ref="name"/>
        <xs:element 
            name="vorname" 
            type="xs:string"/>
        <xs:element 
            name="Gehalt" 
            type="xs:decimal"/>
        <xs:element 
            ref="Kauf" 
            maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element name="Kauf">
    <xs:complexType>
      <xs:sequence>
        <xs:element 
            name="Gesamt" 
            type="xs:decimal"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
  <xs:element 
            name="name" 
            type="xs:string"/>
</xs:schema>

Folgeprogramme lesen den Inhalt der XML-Datenfelder aus und verarbeiten sie weiter. Wenn einzelne Datenfelder jedoch nicht vorhanden sind oder nicht in dem vom Programmierer erwarteten Datentyp übernommen werden können, dann gerät die korrekte Auswertung in Gefahr. Der Programmieraufwand zur Behandlung fehlerhaften Daten-Inputs kann sehr hoch und teuer werden, umso mehr, wenn gleich mehrere (Hunderte?) unterschiedliche Programme auf diesem Dateninput aufsetzen.

Insofern kann eine vorgeschaltete XML-Input-Validierung Abweichungen aufzeigen, die zur systematischen Weiterentwicklung der Folgeprogramme beitragen sollten. Möglich ist auch, die gesamte Weiterverarbeitung komplett zu stoppen, wenn der XML-Input nicht valide ist. Das erspart eine aufwendige Struktur- und Typprüfung in der Verarbeitungslogik, bewahrt aber nicht vor grundsätzlichen Programmierfehlern oder vor mangelhaften Anschlusstests.

XML-Verarbeitung ohne vorherige Validierung?

Warum sollte man das tun? Der Sinn der XML-Schema-Validierung besteht doch gerade darin, ein XML-Dokument hinsichtlich seiner Datenstruktur (Elemente, Attribute, Namespaces) und deren Datentypen zu prüfen. Dabei geht es weniger darum, dem Datenlieferanten Fehler und Fahrlässigkeit nachweisen zu können. Wichtiger ist, die Annahmen und Voraussetzungen der Folgeprogrammierung abzusichern und damit weitergehenden Informationsverlust zu vermeiden. Wieso sollten Sie darauf verzichten?

Eine XSD-Validierung vor Beginn des Konvertierungsprozesses macht nur Sinn, wenn der Workflow gestoppt werden soll, sobald die XSD-Validierung auf einen Fehler läuft: Die XSL-Konvertierung würde also erst gar nicht starten. Dafür kann es triftige Gründe geben.

Ein Grund mag sein: Sie verfolgen eine kompromisslose Politik, dass Input-Streams entweder vollständig fehlerfrei oder vollständig unbrauchbar sind. Entweder "stimmt" das Input-Dokument hundertprozentig oder gar nicht. Eine Grauzone "dazwischen" kann es nicht geben. Wenn das Dokument schon in Teilen nicht stimmt, dann ist es wahrscheinlich, dass sich Fehler auch in jenen Bereichen eingeschlichen haben, die Sie für die Anschlusskonvertierung benötigen. Also stoppen Sie lieber das Ganze, bevor Sie sich die Mühe machen, durch eine aufwendige Implementierung Datenfehler automatisiert zu erkennen und zu beheben.

Zudem ist eine solche vorherige XSD-Validierung auch ein Stück Sicherheit, wenn Ihre Implementierungslogik konsequent auf einem bestimmten XML-Schema des Datenlieferanten aufbaut, quasi fest damit "verdrahtet" ist. Sendet der Datenlieferant nun ohne Vorwarnung andere Datenstrukturen an Ihr System, dann besteht keine Gefahr einer zufälligen Fehlkonvertierung mit Informationsverlust, die sich ohne Vorprüfung ergeben könnte.


<Menschen>
 <xs:schema id="Menschen" 
   xmlns:xs="http://www.w3.org/2001/XMLSchema" >
  <xs:element name="Menschen">
   <xs:complexType>
    <xs:sequence minOccurs="0" maxOccurs="unbounded">
     <xs:element name="Mensch">
      <xs:complexType>
       <xs:sequence>
        <xs:element name="id" type="xs:int" />
        <xs:element name="name" type="xs:string" />
        <xs:element name="vorname" type="xs:string" />
        <xs:element name="Gehalt" type="xs:string" />
        <xs:element name="idOrt" type="xs:int" />
       </xs:sequence>
      </xs:complexType>
     </xs:element>
    </xs:sequence>
   </xs:complexType>
   <xs:unique name="primarykey">
    <xs:selector xpath=".//Mensch" />
    <xs:field xpath="id" />
   </xs:unique>
  </xs:element>
 </xs:schema>
 <Mensch>
  <id>1</id>
  <name>Holzflos</name>
  <vorname>Hugo</vorname>
  <Gehalt>234.56</Gehalt>
  <idOrt>1</idOrt>
 </Mensch>
 <Mensch>
  <id>2</id>
  <name>Sagblos</name>
  <vorname>Stefan</vorname>
  <Gehalt>321.45</Gehalt>
  <idOrt>1</idOrt>
 </Mensch>
 <Mensch>
  <id>3</id>
  <name>Sorglos</name>
  <vorname>Siggi</vorname>
  <Gehalt>987.58</Gehalt>
  <idOrt>1</idOrt>
 </Mensch>
</Menschen>

Auf der anderen Seite ist die Aussagekraft der Schemavalidierung begrenzt, da sich XML-Schemata zwar sehr präzise, aber auch ebenso lax definieren lassen.

Schwierig wird es, wenn das verwendete XML-Schema keine Fakten erzwingt, die in der XSLT-Logik verwendet werden können, sondern je nach den Prozesserfordernissen "zurechtgebogen" und dabei entschärft wird. Ein einfaches Beispiel: Das XML-Element wert sei im XML-Schema als xs:decimal deklariert. Korrekt im Sinn vom XML-Schema wäre daher ausschliesslich <wert>123.45</wert>. Die XML-Daten kommen jedoch teilweise auch im Format <wert>123,45</wert>. Daher würde die XML-Schema-Validierung auf einen Fehler laufen, der anschließende Konvertierungsprozess würde gestoppt.

Um das zu vermeiden, bleibt nur, das XML-Schema so zurechtzubiegen, dass es als Dezimaltrenner sowohl ein Komma als auch einen Punkt akzeptiert. Das wäre möglich durch eine Umdefinition von wert vor xs:decimal auf xs:string mit einem pragmatisch gedrechselten Pattern. Ein solchermaßen "entschärftes" XML-Schema zieht in der XSLT-Konvertierungsphase zusätzlichen Programmieraufwand nach sich: 123,45 muss erst nach 123.45 konvertiert werden, bevor Sie damit korrekt kalkulieren können.

Die Möglichkeiten, die XML-Schema bietet, werden selten vollständig genutzt. Ich habe wiederholt XSDs gesehen, deren Elemente und Attribute pauschal als xs:string definiert wurden, sogar einige, wo nur das Root-Element und einige allgemeine Strukturdefinitionen, der Rest aber als deklariert wurde. Auch eine Deklaration gegenseitiger Abhängigkeiten im Sinne von Primär-/Fremdschlüsseln habe ich in XSDs selten gefunden, obwohl diese in den XML-Dokumenten durchaus vorhanden waren: Diese Dinge mussten dann später im Konvertierungsprozess aufwendig geprüft werden. Das Argument, "Wir haben ein XML-Schema", sagt daher nichts aus über die Qualität des XML-Schemas, das als Stopp-Kriterium für umfangreiche Konvertierungsstrecken dienen soll.

Ebenfalls problematisch ist, dass die Eingangsvalidierung sich grundsätzlich auf das gesamte XML-Dokument bezieht. Die Anschlussprogrammierung wertet aber nur selten mehr als zwanzig Prozent der Eingangsdaten aus. Wenn die Validierung wegen einiger Fehler schiefgeht, die bei der Datenauswertung ohnehin igoriert werden, und deswegen die Folgekonvertierung nicht startet, dann kann das folgenreich sein. Nehmen Sie an, ein Kunde sendet Ihnen einen Millionenauftrag in XML-Format. Sie werden sich dreimal überlegen, den Auftrag abzulehnen wegen einiger Fehler, die für die automatisierte Verarbeitung irrelevant sind.

Zudem wird die Qualität der gesamten Datenlage beim Datenempfänger durch teilweise gestoppte Eingangskonvertierung nicht unbedingt erhöht. Es kommt (gar nicht selten) vor, dass aktuell eingehende Datenströme sich auf andere Daten beziehen, die zeitversetzt entweder bereits geflossen oder noch zu erwarten sind. Wenn es dem Datenempfänger wichtig ist, einen möglichst geschlossenen, vollständigen Datenkontext über einen ganzen Zeitraum verfügbar zu haben, dann kann die Eingangsvalidierung sehr hinderlich sein. Es können sich Datenlücken ergeben, die die Aussagekraft einschränken.

Dabei ist es nicht notwendig, eine "schlechte Datenqualität" der Inputstreams vorher durch Validierung abzufangen, um die Folgekonvertierung nicht zu belasten. Das wäre nur dann zwingend erforderlich, wenn die Folgeanwendung selbst nicht in der Lage ist, eine suboptimale Datenqualität abzuwehren. Das ist bei XSLT 2.0, XSLT 3.0, XQuery 3.0 und den korrespondierenden XPath-Standards aber nicht der Fall.

Eine eher pragmatische Sicht der Dinge ist daher, ob sich auch ein teilweise fehlerhafter Inputstream sinnvoll verwenden lässt, indem man auf die vorgeschaltete Validierung verzichtet und diese stattdessen in die verlegt. Dann ist es möglich, irrelevante Daten zu ignorieren und sich auf die tatsächlich benötigten Informationen zu konzentrieren. Die Möglichkeiten sind ab XSLT 2.0 vorhanden; sie müssen nur genutzt (und ausreichend getestet) werden.

wg / 14. Juli 2018



Fragen? Anmerkungen? Tips?

Bitte nehmen Sie Kontakt zu mir auf.






Vielen Dank für Ihr Interesse an meiner Arbeit.


V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de

www.wilfried-grupe.de/Validierung.html