Hierarchisierung der Fehler

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

Bei ausführlicheren Schematron-Schemata kann die Anzahl der Regeln und damit die der zu prüfenden Fehler erheblich zunehmen. Kommt eine große Instanz hinzu, die viele dieser Fehler enthält, kann die Liste der aufgezeigten Fehler in einem Bericht schnell unübersichtlich werden. Sind alle Fehler gleichwertig, ist dies weniger problematisch, da die Fehler der Reihe nach behoben werden können. Schematron hat den Vorzug, auch für Warnungen oder Hinweise eingesetzt werden zu können, die nur unter bestimmten Umständen behoben werden müssen. Hierzu können die Fehler des Schematron-Schemas in eine Fehlerhierarchie untergliedert werden.

Einteilung in Fehlerhierarchien

Mit Hilfe des role-Attributs (für <assert>, <report> oder <rule>) können den Tests mit bestimmten Schlüsselwörtern unterschiedliche Wertigkeiten zugewiesen werden. Sowohl die Schlüsselwörter als auch deren Auswirkung sind nicht im Schematron-Standard definiert, sondern werden von der unterstützenden Implementierung vorgegeben, weshalb hier keine allgemeingültigen Beispiele vorgestellt werden können. Als Beispiel soll für uns der oxygen-Editor dienen, der ab der Version 11.1. dieses Attribut unterstützt.

Er bietet folgende Schlüsselwörter an:

Schlüsselwort Fehler-Kategorie
fatal Kritischer Fehler
error Fehler
warn Warnung
warning
info Information
information

Tabelle: oXygen-Schlüsselwörter für Fehlermeldungen

Entsprechend der Wertigkeit der Tests werden die auftretenden »Fehler« im oXygen-Editor unterschiedlich dargestellt und behandelt. Ein Fehler wird rot, eine Warnung gelb unterringelt und eine Information gar nicht im Eingabefenster angezeigt, sondern nur im Fehlerprotokoll eines Info-Berichts aufgeführt.

In diese Fehler-Kategorien können nun die Regeln bzw. Tests unseres Arche-Schemas eingeordnet werden. Hierzu müssen die definierten Tests zuerst kategorisiert werden.

Fehler-Kategorie Wert des Tests
Kritischer Fehler Einhaltung der Nutzlast
Fehler Einhaltung des maximalen Reproduktionsalters
Trennung in Pflanzen- und Fleischfresser
Weniger als zwei Tiere einer Art
Warnung Mehr als zwei Tiere einer Art
Information Mehr als sechs Tiere in einem Zimmer

Tabelle: Fehlerkategorien für das Arche-Beispiel

Wird die Arche überlastet und besteht dadurch die Gefahr des Kenterns, ist das weitere Leben auf der Erde gefährdet. Daher handelt es sich um einen kritischen Fehler. Wird nur eine Tierart bedroht, haben wir die entsprechenden Fehler als normale Fehler eingestuft. Bei mehr als zwei Tieren einer Art wird jedoch das Weiterleben einer Tierart nicht bedroht. Der Fehler hat also niedrigere Priorität. Bei der Meldung, die Zimmer aufzeigt, in denen mehr als sechs Tiere untergebracht sind, soll es sich um eine Empfehlung handeln – also eine Zusatzinformation.

Im Schematron-Code ist diese Einteilung nun wie folgt umzusetzen:

<schema xmlns="http://purl.oclc.org/dsdl/schematron">
  <ns uri="http://www.schematron.info/arche" prefix="arc"/>
  <Pattern>
    <rule context="arc:tier[@geschlecht='männlich']">
      <report role="error" test="number(arc:alter) &gt; number(//arc:maxReproduktionsalter/arc:tier_art [arc:name=current()/arc:art]/ arc:männlich)">Das Männchen ist zu alt, Noah! Es wird sich nicht mehr fortpflanzen können. Sorge für die natürliche Auslese.</report>   (1)
    </rule>
  </pattern>
  <pattern>
    <rule context="arc:nutzlast">
      <report role="fatal" test="number(//arc:nutzlast) &lt; sum(// arc:gewicht)">Noah, du hast zu viele Tiere an Bord. Die Ladung überschreitet die Nutzlast deiner Arche.</report>   (1)
    </rule>
  </pattern>
  <pattern>
    <rule role="error" context="arc:tier[@fleischfresser='ja']">   (2)
      <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>
  <pattern>
    <rule context="arc:tier">
      <report role="warn" test="count(//arc:tier[arc:art=current()/arc:art]) &gt; 2">In der Arche gibt es mehr als zwei Tiere dieser Art.</report>   (1)
      <report test="count(parent::*/arc:tier[arc:art=current()/arc:art]) &lt; 2">In dieser Unterkunft gibt es weniger als zwei Tiere dieser Art.</report>   (3)
      <assert test="count(parent::*/arc:tier[arc:art=current()/arc:art][@geschlecht='männlich'])=1">Ein Paar muss immer aus einem Männchen und einem Weibchen bestehen.</assert>   (3)
    </rule>
  </pattern>
  <pattern>
    <rule context="arc:zimmer">
      <report role="info" test="count(arc:tier) &gt; 6">Noah, bringst du zu viele Tiere in einem Zimmer unter, könnte sich das schlecht auf die Zimmergemeinschaft auswirken! Du solltest nicht mehr als 6 Tiere in einem Zimmer unterbringen.</report>   (1)
    </rule>
  </pattern>
</schema> 

(1) Hier werden die Tests den Fehler-Kategorien kritischer Fehler, normaler Fehler, Warnung oder Information zugeordnet, jeweils mit den Schlüsselwörtern fatal, error, warn oder info.

(2) Da das role-Attribut einem <rule>-Element hinzugefügt wurde, wird der role-Wert (hier error) auf alle Tests dieser Regel vererbt, denen kein anderer role-Wert zugewiesen wurde.

(3) Ein Test ohne role-Attribut wird von oXygen als normaler Fehler behandelt.

Verschiedene Phasen festlegen

Schematron erlaubt dem Entwickler verschiedene sogenannte Phasen zu definieren. Eine Phase dient als Container für eine Sammlung an Patterns, die einen bestimmen Prüfungszweck erfüllen. Bei der Validierung mit der Angabe einer Phase werden dann nur die Schematron-Regeln der aktiven Patterns zur Überprüfung herangezogen – alle inaktiven werden ignoriert.

Wurde mittels der phase-Konstruktion mindestens eine Phase definiert, kann die Implementierung den Benutzer vor der Validierung fragen, welche Phase angewandt werden soll. Standardmäßig soll die Implementierung auch eine Phase anbieten, bei der alle Patterns aktiv sind.

Mittels dieser Konstruktion kann nun eine ähnliche Hierarchisierung stattfinden wie mit dem role-Attribut. Beispielsweise kann eine Phase definiert werden, die nur kritische Fehler anzeigt und alle anderen Fehler niedrigerer Priorität ausblendet. Denkbar ist etwa eine Phase für normale Fehler, die Warnungen und Informationen ausblendet, und eine weitere, die alle Informationen verbirgt.

Wir können bei Schematron nur komplette Patterns aktivieren und nicht einzelne Regeln oder Tests. Stehen zwei Regeln in einem Pattern, müssen immer beide aktiv oder eben inaktiv sein. Bei der Gliederung der Regeln in Patterns muss somit auch berücksichtigt werden, dass möglicherweise eine differenzierte Aktivierung der Regeln erwünscht ist.

Um nun eine Phase zu definieren, müssen zunächst alle Patterns, die aktiviert werden sollen, eine ID erhalten. Zur Vereinfachung soll unser Arche-Schematron auf zwei Patterns gekürzt werden:

<schema xmlns="http://purl.oclc.org/dsdl/schematron"> 
  <ns uri="http://www.schematron.info/arche" prefix="arc"/> 
  <pattern id="nutzlast">   (1) 
    <rule context="arc:nutzlast"> 
      <report test="number(//arc:nutzlast) &lt;  sum(//arc:gewicht)" role="fatal">Noah, du hast zu viele Tiere an Bord. Die Ladung überschreitet die Nutzlast deiner Arche.</report> 
    </rule> 
  </pattern> 
  <pattern id="zimmerGemeinschaft">   (2) 
    <rule context="arc:zimmer"> 
      <report test="count(arc:tier) &gt; 6" role="info">Noah, bringst du zu viele Tiere in einem Zimmer unter, könnte sich das schlecht auf die Zimmergemeinschaft auswirken! Du solltest nicht mehr als 6 Tiere in einem Zimmer unterbringen.</report> 
    </rule> 
  </pattern> 
</schema> 

(1) Dem Pattern für die Nutzlast wird mit dem id-Attribut die ID nutzlast zugefügt. Geplant ist, eine Phase zu definieren, die nur dieses Pattern aktiviert.

(2) Das andere Pattern benötigt eigentlich keine ID, da sie in der einzig zu definierenden Phase nicht aktiviert werden soll. Der Vollständigkeit halber wurde hier dem Pattern die ID zimmerGemeinschaft zugewiesen.

Eine Phase wird mit dem Top-Level-Element <phase> definiert. Sie benötigt ein id-Attribut, damit die Implementierung bei der Auswahl der Phase darauf referenzieren kann. Diese ID wird auch als Anzeigename bei der Phasen-Auswahl verwendet. Innerhalb des <phase>-Elements wird nun mit jedem <active>-Element genau ein Pattern für diese Phase aktiviert. Die Zuordnung erfolgt über das Attribut pattern, dessen Wert die ID des zu aktivierenden Patterns sein muss.

Im Folgenden soll für die gekürzte Version des Arche-Schematrons eine Phase definiert werden, die nur das Nutzlast-Pattern aktiviert:

<schema xmlns="http://purl.oclc.org/dsdl/schematron">
  <ns uri="http://www.schematron.info/arche" prefix="arc"/>
  <phase id="kritischeFehler">   (1)
    <active pattern="nutzlast"/>   (2)
  </phase>
  <pattern id="nutzlast">
    <rule context="arc:nutzlast">
      <report test="number(//arc:nutzlast) &lt; sum(//arc:gewicht)" role="fatal">Noah, du hast zu viele Tiere an Bord. Die Ladung überschreitet die Nutzlast deiner Arche.</report>
    </rule>
  </pattern>
  <pattern id="zimmerGemeinschaft">
    <rule context="arc:zimmer">
      <report test="count(arc:tier) &gt; 6" role="info">Noah, bringst du zu viele Tiere in einem Zimmer unter, könnte sich das schlecht auf die Zimmergemeinschaft auswirken! Du solltest nicht mehr als 6 Tiere in einem Zimmer unterbringen.</report>
    </rule>
  </pattern>
</schema> 

(1) Das id-Attribut gibt eine ID an, die auch als Anzeigename verwendet wird. Deshalb sollte sie nicht nur eindeutig, sondern auch semantisch aussagekräftig sein. Hier wurde kritischeFehler gewählt, da diese Phase Patterns bzw. Regeln aktiviert, die alle kritischen Fehler enthalten.

(2) Das <active>-Element aktiviert für diese Phase das Pattern mit der ID nutzlast.

Schauen wir uns an, wie oXygen mit diesem Schematron-Schema umgeht:

Phasen in oXygen

Abbildung: Phasen in oXygen

Bei der Validierung wird zunächst ein Auswahlfenster angezeigt. Hier werden die Phasen #ALL und kritischeFehler angeboten. Bei der Phase #ALL handelt es sich um die standardmäßig implementierte Phase, die alle Patterns aktiviert. Eine Validierung mit der #ALL-Phase entspricht der Validierung ohne definierte Phase. Wählen wir (wie in der Abbildung) kritischeFehler aus, erfolgt eine Validierung anhand der Phase kritischeFehler und die Information über zu volle Zimmer wird ausgeblendet.

Im Folgenden soll nun für jede Fehler-Hierarchieebene des kompletten Arche-Schemas eine Phase implementiert werden. In diesem Schema soll auch eine Default-Phase festgelegt werden. Bei oXygen bedeutet dies, dass die Default-Phase zuerst ausgewählt ist. Bei anderen Implementierungen kann dies jedoch unterschiedlich gelöst sein.

<schema xmlns="http://purl.oclc.org/dsdl/schematron" defaultPhase="alleFehler">   (1)
  <ns uri="http://www.schematron.info/arche" prefix="arc"/>
  <phase id="kritischeFehler">
    <active pattern="nutzlast"/>
  </phase>
  <phase id="alleFehler">   (2)
    <active pattern="nutzlast"/>
    <active pattern="Repro"/>
    <active pattern="fressSchutz"/>
    <active pattern="vonJedemEinPaar"/>
  </phase>
  <phase id="keineInfos">   (2)
    <active pattern="nutzlast"/>
    <active pattern="Repro"/>
    <active pattern="fressSchutz"/>
    <active pattern="vonJedemEinPaar"/>
    <active pattern="gleichberechtigung"/>
  </phase>
  <pattern id="Repro">   (3)
    <rule context="arc:tier[@geschlecht='männlich']">
      <report test="number(arc:alter) &gt; number(//arc:maxReproduktionsalter /arc:tier_art[arc:name=current()/arc:art] /arc:männlich)" role="error">Das Männchen ist zu alt, Noah! Es wird sich nicht mehr fortpflanzen können. Sorge für die natürliche Auslese.</report>
    </rule>
    <rule context="arc:tier[@geschlecht='weiblich']">
      <report test="number(arc:alter) &gt; number(//arc:maxReproduktionsalter/arc:tier_art[arc:name=current()/arc:art]/arc:weiblich)"role="error">Das Weibchen ist zu alt, Noah! Es wird sich nicht mehr fortpflanzen können. Sorge für die natürliche Auslese.</report>
    </rule>
  </pattern>
  <pattern id="nutzlast">   (3)
    <rule context="arc:nutzlast">
      <report test="number(//arc:nutzlast) &lt; sum(//arc:gewicht)" role="fatal">Noah, du hast zu viele Tiere an Bord. Die Ladung überschreitet die Nutzlast deiner Arche.</report>
    </rule>
  </pattern>
  <pattern id="fressSchutz">   (3)
    <rule context="arc:tier[@fleischfresser='ja']" role="error">
      <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>
    <rule context="arc:tier">
      <report test="parent::*/arc:tier/arc:gewicht &lt; (arc:gewicht div 10)" role="error">Noah, das Tier ist zu schwer für seine Zimmergenossen! Es könnte einen zertrampeln.</report>
    </rule>
  </pattern>
  <pattern id="vonJedemEinPaar">   (3)
    <rule context="arc:tier">
      <report test="count(parent::*/arc:tier[arc:art=current()/arc:art]) &lt; 2">In dieser Unterkunft gibt es weniger als zwei Tiere dieser Art.</report>
      <assert test="count(parent::*/arc:tier[arc:art=current()/arc:art][@geschlecht='männlich'])=1">Ein Paar muss immer (nur) aus einem Männchen und einem Weibchen bestehen.</assert>
    </rule>
  </pattern>
  <pattern id="gleichberechtigung">   (3) (4)
    <rule context="arc:tier">
      <report test="count(//arc:tier[arc:art=current()/arc:art]) &gt; 2"role="warn">In der Arche gibt es mehr als zwei Tiere dieser Art.</report>
    </rule>
  </pattern>
  <pattern id="zimmerGemeinschaft">   (3)
    <rule context="arc:zimmer">
      <report test="count(arc:tier) &gt; 6" role="info">Noah, bringst du zu viele Tiere in einem Zimmer unter, könnte sich das schlecht auf die Zimmergemeinschaft auswirken! Du solltest nicht mehr als 6 Tiere in einem Zimmer unterbringen.</report>
    </rule>
  </pattern>
</schema>


(1) Hier wird dem Wurzelelement <schema> das Attribut defaultPhase hinzugefügt. Als Wert wird die ID der Phase übergeben. Hier wird die zweite definierte Phase als Default-Phase mit der ID alleFehler gesetzt.

(2) Zusätzlich wird nun die Phase für sämtliche Fehler mit der ID alleFehler und jene für Fehler inklusive Warnungen (aber ohne Informationen) mit der ID keineInfos definiert.

(3) Hier wird jedem Pattern eine ID zugewiesen.

(4) Die Warnung, die dafür sorgt, dass nicht mehr als ein Paar von jeder Tierart in der Arche einquartiert wird, war bisher im Pattern mit der ID vonJedemEinPaar versehen. Da dieses Pattern normale Fehler enthält und die Warnung von anderen Phasen aktiviert werden soll, muss ein neues Pattern erzeugt werden, das die Warnung als einzige <rule> enthält. So sind Warnungen und normale Fehler durch unterschiedliche Patterns voneinander getrennt.

Betrachten wir nun die Auswirkungen in oXygen. Es stehen nicht nur entsprechend mehr Phasen zur Auswahl, sondern auch die Reihenfolge ist nicht gleich der <phase>-Elemente im Schematron-Schema:

Erweiterte Phasen für das Arche-Schema

Abbildung: Erweiterte Phasen für das Arche-Schema

Dies liegt an der definierten Default-Phase. Sie wurde an den Anfang der Liste gestellt und als Voreinstellung ausgewählt.

Beachten Sie, dass wir dies nur als Beispiel für eine Anwendung der phase-Konstruktion vorgestellt haben. Eine Einrichtung von Phasen muss nicht grundsätzlich zur Hierarchisierung der Fehler führen. Beispielsweise könnte Noah ja auch einen Beauftragten für Artenschutz ernennen, der sich nur darum kümmert, dass keine Tierart während der Fahrt mit der Arche ausstirbt. In diesem Fall ist er nur befugt, auf bestimmte Fehler zu prüfen. So können verschiedene Sichtweisen auf ein XML-Dokument entstehen.

   

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