
| 1 / 27 |
Erinnerung: Mittel zur Modellierung von Dokumenttypen
Alle sind kontextfreie, grammatik-orientierte Sprachen.
Das bedeutet:
| 2 / 27 |
anteil-Attribute aller
teilhaber-Elemente muss 100 ergeben.
Das XML-Dokument könnte also in etwa so aussehen:
<firma>
<teilhaber anteil="65">
<name>Ludwig Müller</name>
...
</teilhaber>
<teilhaber anteil="15">
<name>Liselotte Müller</name>
...
</teilhaber>
<teilhaber anteil="20">
<name>Fritz Müller</name>
...
</teilhaber>
</firma> |
| 3 / 27 |
kenntnis den Wert
"experte" besitzt, muss ein zweites Attribut
zugriff mit einer ganzen Zahl zwischen 0 und 100
angegeben werden. Für andere Werte von kenntnis ist
das zugriff-Attribut nicht zulässig.
Folgende Beispiele verstoßen gegen diese Regel
<nutzer kenntnis="experte"> ... </nutzer><nutzer kenntnis="anfänger" zugriff="80"> ... </nutzer>
|
abschnitt darf sich rekursiv selbst
enthalten, allerdings maximal drei Ebenen tief.
IDREF-Attribut referenzierte Element
muss von einem bestimmten Typ sein.
Beispielsweise könnte man sich vorstellen, dass Hunde und Katzen jeweils über eigene Elemente mit einer eigenen ID beschrieben werden. Ein Tierhalter verweist über diese ID auf sein Tier. Allerdings sollen dann Katzenhalter auch nur Katzen besitzen und Hundehalter nur Hunde.
Konsequenz: Überprüfung dieser Bedingungen muss anwendungsspezifisch erfolgen.
Das heißt, eine Anwendung, die das zu verarbeitende Vokabular kennt, muss die Überprüfung selbst vornehmen.
| 4 / 27 |
Beobachtung: Verstöße gegen Gültigkeitsregeln können als Ergebnis einer Formatierung bzw. Transformation gemeldet werden.
Beispiel: Die Summe der Werte der
anteil-Attribute aller teilhaber-Elemente
muss 100 ergeben.
<xsl:template match="firma">
<xsl:if test="sum(teilhaber/@anteil) != 100">
Die Summe der Anteile ergibt nicht 100.
</xsl:if>
</xsl:template> |
Validierung ist unabhängig von der Anwendung möglich!
| 5 / 27 |
Allgemeines Schema:
<xsl:template match="Kontext">
<xsl:if test="Bedingung">
Fehlermeldung
</xsl:if>
<xsl:apply-templates />
</xsl:template>
xsl:apply-templates wird benötigt, damit weitere
(in weiteren eigenen Vorlagen spezifizierte) Tests für die Kindelemente
ebenfalls ausgeführt werden.
Für das Beispiel:
Kontext ist "Firma"Bedingung ist
"sum(teilhaber/@anteil) != 100"| 6 / 27 |
zusätzlich:
<xsl:template match="text()" />
Die voreingestellte Vorlage für Textknoten muss überschrieben werden, damit Text aus dem Dokument nicht in die Ausgabe kopiert wird.
Ergebnis:
Ein Dokument ist fehlerfrei, wenn ein Validierungs-Stylesheet
keine Ausgaben, d.h. keine Fehlermeldungen erzeugt.
| 7 / 27 |
Schematron entstand als persönliches Projekt von Rick Jeliffe. Inzwischen beteiligen sich mehrere Leute daran, es ist aber kein "offizielles" Produkt oder eine Spezifikation irgendeiner Organisation. Zum Teil hat Schematron noch experimentellen Charakter.
vereinfachte Grundstruktur eines Schematron-Schemas:
- <schema>
- <title>?
- <phase>*
- <active>+
- <pattern>+
- <rule>+
- <extends>*
- (<assert> | <report>)+
- <diagnostics>?
- <diagnostic>+
Das Wurzelelementschemaenthält optional einen Titel (title), mehrephase-Elemente, anschließend ein oder mehrere Muster (pattern-Elemente) und zuletzt optional eindiagnostics-Element. Der Begriff "Muster" ist hier als "Gültigkeitsmuster" oder "Anwendungsfall" zu verstehen. Jedes Muster enthält mehrere Regeln (rule), die wiederum optional ausextends-Elementen für Basis-Regeln sowie Zusicherungen (assert) oder Meldungen (report) bestehen. Inhalt dieser letzten Elemente ist die jeweilige Fehlermeldung.
| 8 / 27 |
Das Summen-Beispiel als minimales Schematron-Schema:
<schema
xmlns="http://www.ascc.net/xml/schematron">
<title>Validierungsschema für Firmen</title>
<pattern name="Anteilsverteilung">
<rule context="Firma">
<assert test="sum(teilhaber/@anteil) = 100">
Die Summe aller Anteile muss 100 ergeben.
</assert>
</rule>
</pattern>
</schema> |
assert drückt hier eine Zusicherung (eine positive
Bedingung) aus, deren Verletzung gemeldet wird.
Mit report kann man negative Bedingungen ausdrücken,
deren Zutreffen gemeldet wird.
Das bedeutet letztendlich, dassassertundreportzueinander entgegengesetzte Elemente sind. Man könnte vollständig auf eines der beiden verzichten und den Test jeweils geeignet negieren.
Das vorgestellte XSLT-Beispiel entspricht daher eigentlich
<report test="sum(teilhaber/@anteil) != 100">
| 9 / 27 |
Ein Schematron-Schema kann in ein Validierungs-Stylesheet transformiert werden:
Das Schematron-Schema sieht so aus, wie auf der vorherigen Seite dargestellt. Im Ergebnis der Transformation entsteht ein Validierungs-Stylesheet, das im wesentlichen aus Vorlagen, wie auf Seite 4 dargestellt, besteht. Der Schematron-Präprozessor ist das "Programm" (die Transformations-Datei), das die Regeln (Vorlagen) für die Umwandlung des Schematron-Schemas in das Validierungs-Stylesheet enthält. Dieses kann von der Schematron-Homepage geladen werden.
XML-Dokument steht schließlich für das zu überprüfende Dokument, also beispielsweise für das mit den Firmen-Daten.
| 10 / 27 |
Formulierung von Zusicherungen:
<assert test="Zusicherung"> Text beim Feststellen einer Verletzung </assert>
Zusicherung ist eine positive Bedingung.
Formulierung von Fehlerzuständen:
<report test="Fehlerzustand"> Text beim Zutreffen des Zustandes </report>
Fehlerzustand ist eine negative
Bedingung.
| 11 / 27 |
Erläuterung zu den Fehlermeldungen:
Hinzufügen eines diagnostics-Attributes
<assert test="..." diagnostics="Namen"> <report test="..." diagnostics="Namen">
Diagnose-Meldungen werden dann separat unter diagnostics
aufgeführt:
<diagnostics>
<diagnostic id="Name">
Diagnose-Meldung
</diagnostic>
<!-- weitere Diagnosen -->
...
</diagnostics>
Der Text direkt unterassertoderreportdient nur als kurze Fehlermeldung. Ausführlichere Fehlerbeschreibungen werden davon getrennt aufgeschrieben. Sie können dann beispielsweise (abhängig vom Schematron-Werkzeug) nur bei Bedarf angezeigt werden.
Imdiagnostics-Attribut vonassertoderreportkönnen mehrere Diagnose-Namen angegeben werden, die dann alle benutzt werden.
| 12 / 27 |
Innerhalb von assert- oder report-Meldungen
kann das leere Element name verwendet werden.
Bedeutung: der Name des Kontextknotens
(d.h. <xsl:value-of select="name(.)" />)
Innerhalb der Diagnose-Texte kann das leere Element
value-of mit einem Attribut select
verwendet werden.
Bedeutung: xsl:value-of
<rule context="html:*">
<report test="@style">
Anstelle von style sollte in <name />
ein class-Attribut verwendet werden.
</report>
</rule> |
Diese Regel überprüft alle Elemente aus dem HTML-Namensraum,
ob sie ein style-Attribut besitzen, und meldet dies
gegebenenfalls.
| 13 / 27 |
Zusammenfassung der Tests innerhalb einer Regel:
<rule context="XSLT-Pattern"> assert- oder report-Elemente </rule>
Das context-Attribut gibt an, für welche Knoten die
Bedingungen überprüft werden sollen.
<rule context="zubereitung/zutat">
<assert test="@ref">
Jede Zutat in der Anleitung muss einen
Verweis auf die Zutatenliste enthalten.
</assert>
</rule> |
| 14 / 27 |
Regeln können als abstrakt gekennzeichnet sein, d.h. sie dienen als Vorlage für andere konkrete Regeln:
<rule abstract="true" id="Name"> assert- oder report-Elemente </rule> <rule context="Kontext"> <extends rule="Name" /> ... </rule>
Abstrakte Regeln dürfen kein context-Attribut
besitzen.
<rule abstract="true" id="bezeichner">
<assert test="@id">
Das Element <name /> muss ein id-Attribut
besitzen.
</assert>
</rule> |
Diese Regel ist abstrakt und kann für ausgewählte Elemente
(d.h. konkrete Regeln) unter dem Namen bezeichner
wiederverwendet werden.
| 15 / 27 |
Zusammenfassung mehrerer Regeln:
<pattern name="Bezeichnung"> rule-Elemente <pattern>
Ein pattern-Element fasst mehrere Regeln zusammen,
die zu einem Gültigkeitstest gehören. Zum Beispiel:
XSLT-Hintergrund:
Das zu überprüfende XML-Dokument wird für jedes Gültigkeitsmuster
genau einmal durchlaufen.
Das bedeutet auch, dass aus konkurrierenden Regeln innerhalb desselbenpattern-Elements nur eine zum Zuge kommt, nämlich die mit der höchsten Priorität. Das liegt daran, dass für jedesrule-Element einxsl:template-Element im Validierungs-Stylesheet generiert wird.
| 16 / 27 |
Über Phasen kann man festlegen, welche Gültigkeitsmuster tatsächlich überprüft werden sollen.
<phase id="Phasen-Name"> <active pattern="Muster-ID" /> <!-- weitere in dieser Phase aktive Muster --> ... </phase>
Jedes Muster muss dann zusätzlich ein id-Attribut
besitzen, auf das Muster-ID verweist.
Standardmäßig sind alle Muster aktiv. Alternativ kann im
schema-Element auch eine Standard-Phase angegeben
werden:
<schema defaultPhase="Phasen-Name" ... >
| 17 / 27 |
id-Attribut,
darf aber kein ref-Attribut besitzen.
id-Attribut, auf die nicht verwiesen
wurde, sind wohl vergessen worden.
ref-Attribut und darf weder ein id-
noch ein menge-Attribut besitzen.
ref-Attribut muss auf eine Zutat aus dem
gleichen Rezept verweisen.
| 18 / 27 |
Zunächst: Aufteilung in Gültigkeitsmuster
zutat-Elementen und deren
Attributen beschäftigen
Muster 1 enthält strenge Anforderungen, Muster 2 und 3 können als zusätzliche Hinweise verstanden werden.
Festlegung von Phasen:
Eine Phase für alle drei Muster und eine nur für Muster 1
| 19 / 27 |
<schema xmlns="http://www.ascc.net/xml/schematron"
defaultPhase="Struktur">
<title>Validierungsschema für Rezepte</title>
<phase id="alles">
<active pattern="verweis" />
<active pattern="autor" />
<active pattern="text" />
</phase>
<phase id="Struktur">
<active pattern="verweis" />
</phase>
<pattern name="Zutatenverweise" id="verweis">
...
</pattern>
<pattern name="Autorennamen" id="autor">
...
</pattern>
<pattern name="Textlängen" id="text">
...
</pattern>
</schema> |
| 20 / 27 |
Zu den einzelnen Regeln:
Bei den Zutatenverweisen gibt es zwei Kontexte: Zutaten in der Zutatenliste und Zutaten in der Zubereitungsanleitung.
id-Attribut,
darf aber kein ref-Attribut besitzen.
id-Attribut, auf die nicht verwiesen
wurde, sind wohl vergessen worden.
<rule context="zutaten/zutat">
<report test="@ref">
Das Attribut ref ist für Zutaten in der Zutatenliste nicht
erlaubt.
</report>
<assert test="not(@id) or
../../zubereitung/zutat[@ref = current()/@id]">
Auf die id einer Zutat in der Zutatenliste sollte in der
Zubereitungsanleitung auch verwiesen werden.
</assert>
</rule> |
Diese zweite Testbedingung kann man sich folgendermaßen überlegen. Die zu testende Bedingung lautet eigentlich: wenn eine Zutat in der Zutatenliste einid-Attribut besitzt, so muss es auch eine entsprechende Zutat in der Zubereitungsanleitung geben. Oder als logische Formel: "ausAfolgtB". Diese Formel ist äquivalent zu "nichtAoderB", und genau das steht als Test oben imassert-Element.
| 21 / 27 |
Anforderungen an Zutaten in der Zubereitungsanleitung:
ref-Attribut und darf weder ein id-
noch ein menge-Attribut besitzen.
<rule context="zubereitung/zutat">
<assert test="@ref">
Jede Zutat in der Zubereitungsanleitung muss ein ref-Attribut
besitzen.
</assert>
<report test="@id">
Das Attribut id ist für Zutaten in der Zubereitungsanleitung
nicht erlaubt.
</report>
<report test="@menge">
Das Attribut menge ist für Zutaten in der Zubereitungsanleitung
nicht erlaubt.
</report>
...
</rule> |
| 22 / 27 |
Anforderungen an Zutaten in der Zubereitungsanleitung (II)
ref-Attribut muss auf eine Zutat aus dem
gleichen Rezept verweisen.
Noch in der gleichen Regel:
<rule context="zubereitung/zutat">
...
<assert test="not(@ref) or
../../zutaten/zutat[@id=current()/@ref]">
Das Attribut ref muss auf eine der aufgezählten Zutaten verweisen.
</assert>
</rule> |
Würde man not(@ref) weglassen, würde man die
Meldung auch erhalten, wenn das ref-Attribut völlig fehlt.
Das ist zwar auch nicht erlaubt, wird aber bereits durch einen
anderen Test überprüft. Auf diese Weise wird die ansonsten
etwas irreführende Meldung verhindert.
| 23 / 27 |
Zum Muster "Autorennamen":
Zwei Varianten:
<pattern name="Autorennamen" id="autor">
<rule context="idee">
<assert test="count(../idee[@name = current()/@name]) = 1">
Ein Autorenname darf nur einmal pro Rezept angegeben werden.
</assert>
</rule>
</pattern> |
oder alternativ
<pattern name="Autorennamen" id="autor">
<rule context="idee">
<report test="preceding-sibling::idee[@name = current()/@name]">
Ein Autorenname darf nur einmal pro Rezept angegeben werden.
</report>
</rule>
</pattern> |
Welche Variante ist besser? Aus der Formulierung der Bedingung heraus ist die erste vielleicht intuitiver. Die zweite drückt aus: "Ein Autor darf kein zweites Mal genannt werden".
Die zweite Variante hat damit zur Folge, dass nur das zweite Auftreten als Fehler gemeldet wird. In der ersten Variante würde auf alle Vorkommen desselben Namens mit dieser Fehlermeldung reagiert werden.
| 24 / 27 |
Zum Muster "Textlängen":
<pattern name="Textlängen" id="text">
<rule context="zubereitung">
<report test="string-length(.) < 100">
Das Rezept ist verdächtig kurz.
</report>
</rule>
<rule context="rezept/titel">
<report test="string-length(.) > 40">
Der Titel ist zu lang.
</report>
</rule>
</pattern> |
| 25 / 27 |
Einfügen von Diagnosemeldungen
Uns interessiert jeweils die konkrete Länge.
Zusätzlich: diagnostics-Attribut
<pattern name="Textlängen" id="text">
...
<report test="string-length(.) < 100" diagnostics="länge">
...
<report test="string-length(.) > 40" diagnostics="länge">
...
</pattern> |
Anschließend separate Formulierung der Diagnose-Meldung:
<diagnostics>
<diagnostic id="länge">
Die aktuelle Länge ist <value-of select="string-length(.)" />.
</diagnostic>
</diagnostics> |
| 26 / 27 |
Alle Ressourcen rund um Schematron (Implementationen, Spezifikationen, Tutorials) findet man unter der Adresse
http://www.ascc.net/xml/schematron
Diese Adresse wurde ebenfalls als Namensraum für Schematron-Elemente gewählt. Das heißt, dort befindet sich eine RDDL-Beschreibung der Ressourcen für Schematron.
Minimale textbasierte Implementierung:
Schematron-Basic
Zweistufiger Prozess (siehe auch Grafik auf Seite 9):
saxon rezepte.sch schematron-basic.xsl > val.xsl saxon rezepte.xml val.xsl |
Die erste Zeile generiert das Validierungs-Stylesheet (val.xsl) aus der Schematron-Datei (rezepte.sch), die zweite Zeile benutzt dieses Stylesheet dann zur Validierung des XML-Dokuments (rezepte.xml).
Problem dabei: keine Informationen über den Ort der Fehlerquelle (keine Zeilennummern)
| 27 / 27 |
Intelligentere Lösung:
Verweise auf die XML-Quelle
Schematron-Report
Die Verweisziele werden mit Hilfe der XSLT-Funktion
generate-id erzeugt.
saxon rezepte.sch schematron-report.xsl phase=alles > val.xsl saxon rezepte.xml val.xsl > schematron-errors.html saxon rezepte.xml verbid.xsl > schematron-out.html |
Die erste Zeile erzeugt wieder das Validierungs-Stylesheet, wobei hier anstelle derdefaultPhasedie Phase mit dem Namenallesaktiv sein soll.
Die Ausgabe der Validierung wird nun in eine Datei umgeleitet, da gefundene Fehler als HTML-Datei ausgegeben werden (zweiter Aufruf). Die dritte Zeile erzeugt schließlich das HTML-Abbild vonrezepte.xml. Alle benötigten Dateien (schematron-report.xsl,verbid.xsl, ...) findet man über die Schematron-Homepage.
Ergebnis:
Eingabedateien: rezepte2.xml,
rezepte.sch
Frame für die Ausgabe:
schematron-frame.html