Kontextknoten testen

(Auszug aus "Schematron - Effiziente Business Rules für XML-Dokumente", Kapitel 5)

Doch nun wollen wir den auf der vorherigen Seite ausgewählten Kontextknoten testen, um endlich auch eine Fehlermeldung zu generieren. Dem Beispiel ist bereits zu entnehmen, dass das <report>-Element einen Test definiert. Der prinzipielle Aufbau eines Tests ist leicht zu verstehen:

<report test="XPath-Ausdruck">Fehlermeldung</report>

Sollte ein Fehler festgestellt werden, wird der Inhalt des Elements <report> als Fehlermeldung ausgegeben.
Der XPath-Ausdruck im test-Attribut liefert einen booleschen Wert als Rückgabewert. Wird durch XPath kein boolescher Wert zurückgegeben, versucht der Schematron-Parser den Rückgabewert in einen solchen umzuwandeln (casten). Beispielsweise wird ein Ausdruck, der einen oder mehrere Knoten als Ergebnis liefert, in den booleschen Wert true umgewandelt bzw. in den Wert false, wenn es sich um eine leere Knotenmenge handelt.

<schema xmlns="http://purl.oclc.org/dsdl/schematron"> 
  <ns uri="http://www.schematron.info/arche" prefix="arc"/> 
  <Pattern> 
    <rule context="arc:tier[@fleischfresser='ja']"> 
      <report test="parent::*/arc:tier[@fleischfresser='nein']">Es gibt Fleischfresser und Pflanzenfresser in einer Unterkunft. Die Tiere sind keine Nahrungsquelle!</report> 
    </rule> 
  </pattern> 
</schema>

Der obige Test sucht nach einem Element <tier> mit dem Attribut fleischfresser='nein'. Gibt es ein oder mehrere solcher Elemente, wird der Rückgabewert des XPath-Ausdruckes in den Wert true umgewandelt. Andernfalls wird der Wert false zurückgegeben.
Das <report>-Element erlaubt einen Test auf einen Fehler: Gibt der XPath-Ausdruck im test-Attribut den Wert true zurück – also logisch WAHR – ist ein Fehler aufgetreten und die Implementierung gibt die Fehlermeldung aus. Der Wert true wird hier als Fehlerindikator verwendet. Alternativ dazu lässt Schematron das <assert>-Element zu. Der Aufbau ist identisch zum <report>-Test:

<assert test="XPath -Ausdruck">Fehlermeldung</assert>

Der Unterschied zwischen <report>- und <assert >-Tests liegt bei der Verarbeitung des booleschen Wertes. Ein <assert>-Element definiert eine Annahme für den Kontextknoten, die zutreffen muss. Der Wert false – logisch FALSCH – gibt also an, dass diese Annahme nicht erfüllt wird, und löst somit einen Fehler aus. Der Fehlerindikator ist in diesem Fall also der Wert false.
Wer sich intensiv mit XPath beschäftigt, wird jetzt zurecht anmerken, dass die Unterscheidung zwischen <report>- und <assert>-Test eigentlich nicht notwendig ist, da jeder <assert>-Test leicht in einen <report>-Test umgewandelt werden kann, wie die folgenden zwei Regeln zeigen, die identische Funktionsweisen haben:

<rule context="arc:tier[@fleischfresser='ja']"> 
  <report test="parent::*/arc:tier[@fleischfresser='nein']">Es gibt Fleischfresser und Pflanzenfresser in einer Unterkunft. Die Tiere sind keine Nahrungsquelle!</report> 
</rule> 
<rule context="arc:tier[@fleischfresser='ja']"> 
  <assert test="not(parent::*/arc:tier[@fleischfresser='nein'])">Es gibt Fleischfresser und Pflanzenfresser in einer Unterkunft. Die Tiere sind keine Nahrungsquelle!</assert> 
</rule>

Jeder <report>-Test lässt sich mit der XPath-Funktion not() in einen identisch funktionierenden <assert>-Test umwandeln und umgekehrt. Warum also diese Unterscheidung zwischen Asserts und Reports? Dies hat zum einen semantische Gründe, zum anderen einen pragmatischen.
Semantisch unterscheidet Schematron zwischen einem Test auf einen Fehler und einer Annahme. Das kann je nach verwendeter Implementierung zu unterschiedlichen Darstellungen der Fehlermeldungen führen. Es wird also zwischen folgenden beiden Aussagen unterschieden:

  • Fehler (<report>): Der Kontextknoten darf die Bedingung nicht erfüllen.
  • Annahme (<assert>): Der Kontextknoten muss die Bedingung erfüllen.

Aus pragmatischer Sicht fällt der sonst notwendige Einsatz der not()-Funktion in diesem Zusammenhang weg. Entwickler können sich jedoch entscheiden, ob nur <assert>- oder nur <report>-Tests eingesetzt werden oder eine Mischform. Mit dem jetzigen Wissen können wir nun die Schematron-Regel vollständig nachvollziehen:

<schema xmlns="http://purl.oclc.org/dsdl/schematron"> 
  <ns uri="http://www.schematron.info/arche" prefix="arc"/> 
  <pattern> 
    <rule context="arc:tier[@fleischfresser='ja']"> 
      <report test="parent::*/arc:tier[@fleischfresser='nein']">Es gibt Fleischfresser und Pflanzenfresser in einer Unterkunft. Die Tiere sind keine Nahrungsquelle!</report> 
    </rule> 
  </pattern> 
</schema>

Die Regel wird auf alle <tier>-Elemente angewandt, die ein Attribut fleischfresser mit dem Wert ja haben. Für die Regel wurde ein Test definiert, der besagt: Das Elternelement des entsprechenden <tier>-Elements (also <zimmer>) darf kein <tier>-Element enthalten, welches das Attribut fleischfresser mit dem Wert nein hat.
Aus dem XML-Schema ist bekannt, dass Tiere in Zimmern untergebracht sind. Das Elternelement eines <tier>-Elements ist also immer ein <zimmer>-Element. Enthält also das Elternelement des Kontextelements ein weiteres <tier>-Element mit dem Attribut fleischfresser='nein', ist dieses <tier> ein Geschwisterelement des Kontextelements und somit sind beide im gleichen Zimmer untergebracht. Da das Kontextelement bei dieser Regel immer ein fleischfressendes und das gesuchte Element ein pflanzenfressendes Tier ist, widerspricht die Zusammensetzung des Zimmers den Regeln auf der Arche, sobald beim <report>-Test ein pflanzenfressendes Tier gefunden wird.
Ist ein Knoten einmal ausgewählt, kann er, statt wie bisher, anhand mehrerer Tests überprüft werden. Für unser Beispiel erweitern wir die Regel, dass Fleischfresser nicht nur von Pflanzenfressern ferngehalten werden müssen, sondern auch von schwächeren Fleischfressern. Fleischfresser könnten sich ja auch an Schwächeren vergreifen! Die Stärke eines Tieres wird anhand des Körpergewichts festgemacht. Es soll also gelten, dass ein fleischfressendes Tier maximal doppelt so schwer sein darf wie der leichteste Zimmergenosse:

<pattern> 
  <rule context="arc:tier[@fleischfresser='ja']"> 
    <report test="parent::*/arc:tier[@fleischfresser='nein']">Es gibt Fleischfresser und Pflanzenfresser in einer Unterkunft. Die Tiere sind keine Nahrungsquelle!</report> 
    <report test="parent::*/arc:tier/arc:gewicht &lt; (arc:gewicht div 2)">Noah, dieser Fleischfresser ist zu stark (schwer) für seinen Zimmergenossen. Er könnte ihn als Nahrungsquelle benutzen.</report> 
  </rule> 
</pattern>  

Der XPath-Ausdruck prüft, ob ein Geschwisterelement (parent::*/arc:tier) ein <gewicht>-Kindelement (/arc:gewicht) hat, dessen Wert kleiner ist (&lt;) als der halbe Wert des <gewicht>-Elements des Kontextknotens (arc:gewicht div 2).
Soll der Kontextknoten mehrfach getestet werden, werden die Test-Elemente hintereinander gereiht. Hierbei können <assert>- und <report>-Elemente beliebig gemischt werden. Wird in der Instanz ein passender Kontextknoten gefunden, wird er nun anhand aller Tests überprüft. Die Reihenfolge spielt dabei – abgesehen von der Darstellung im Fehlerbericht - keine Rolle.

Anmerkung für XSLT-Profis:
Bei der Skeleton-Transformation zu XSLT werden Tests zu if- bzw. choose-when-otherwise-Konstruktionen konvertiert. Der <report>-Test:

<report test="parent::*/arc:tier[@fleischfresser='nein']">Es gibt Fleischfresser und Pflanzenfresser in einer Unterkunft. Die Tiere sind keine Nahrungsquelle!</report>

wird zu:

<xsl:if test="arc:tier[@fleischfresser='ja']">
   […]
   <!-- Umsetzung der Fehlermeldung in SVRL -->
   […]
</xsl:if>

Ein <assert>-Test dagegen greift auf das <xsl:choose>-Element zurück:

<assert test="not(parent::*/arc:tier[@fleischfresser='nein'])">Es gibt Fleischfresser und Pflanzenfresser in einer Unterkunft. Die Tiere sind keine Nahrungsquelle!</assert>

und wird zu:

<xsl:choose> 
  <xsl:when test="not(parent::*/arc:tier[@fleischfresser='nein'])"/> 
  <xsl:otherwise> 
      […] 
      <!-- Umsetzung der Fehlermeldung in SVRL --> 
      […] 
  </xsl:otherwise> 
</xsl:choose>

   

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema Schematron bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © dpunkt.verlag GmbH 2011
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken. Ansonsten unterliegt dieses Kapitel aus dem Buch "Schematron - Effiziente Business Rules für XML-Dokumente" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

dpunkt.verlag GmbH, Ringstraße 19B, 69115 Heidelberg, fon 06221-14830, fax 06221-148399, hallo(at)dpunkt.de