Aktuelle Remote-Seminare mit Durchführungsgarantie: Etwas Werbung in eigener Sache Termine: 03.08.-05.08.2020, 21.09.-23.09.2020, 23.11.-25.11.2020, 07.12.-09.12.2020, 22.03.-24.03.2021, 07.06.-09.06.2021, 06.09.-08.09.2021XML Einführung und Grundlagen Termine: 06.08.2020, 20.10.2020, 13.11.2020, 10.12.2020, 25.03.2021, 10.06.2021, 09.09.2021XML - der professionelle Überblick Termine: 23.11.-26.11.2020, 29.03.-01.04.2021Einstieg in die Programmierung Termine: 17.08.-19.08.2020, 14.12.-16.12.2020, 07.04.-09.04.2021XML, XSLT, XPath, XSL-FO Einführung Termine: Termin nach WunschEinstieg in C#.NET Termine: 21.09.-23.09.2020, 11.01.-13.01.2021, 26.05.-28.05.2021, 27.09.-29.09.2021VB.NET für Visual Basic-Programmierer Termine: 21.10.-23.10.2020, 14.12.-16.12.2020, 08.03.-10.03.2021, 19.07.-21.07.2021, 23.08.-25.08.2021XML Grundlagen, XSL-FO, SVG, DocBook, DITA Termine: Termin nach WunschJava Grundlagen Termine: 03.08.-06.08.2020, 30.11.-03.12.2020, 29.03.-01.04.2021C++ für Programmiereinsteiger Termine: 10.09.2020, 11.12.2020, 16.03.2021Neues Seminar: XProc Grundlagen Termine: 21.09.-25.09.2020, 01.03.-05.03.2021Java/JSP/HTML/JDBC/XML Komplett Termine: 07.09.-09.09.2020, 21.10.-23.10.2020, 23.11.-25.11.2020, 25.01.-27.01.2021, 12.04.-14.04.2021, 26.07.-28.07.2021Python für Programmiereinsteiger Last-Minute-Seminare, noch freie Plätze XML Einführung und Grundlagen03.08.-05.08.2020 XML, XSLT, XPath, XSL-FO Einführung17.08.-19.08.2020 Python für Programmiereinsteiger07.09.-09.09.2020

XPROC * XSL-FO * SVG * XML * XML-SCHEMA * XPATH * XSL * XSLT 1.0 * XSLT 2.0 * XSLT 3.0 * XQUERY * ANT



XML-Schema / Schematron

Schematron

Schematron

➪ Schematron ist eine sinnvolle Ergänzung zu Datendefinitions-Standards wie DTD, RNG oder XML-Schema. Während Sie zum Beispiel in XML-Schema 1.0 die Namen und Datenstrukturen von Elementen und Attributen sowie deren Datentypen und Namespaces festlegen, können Sie in Schematron zusätzliche Regeln oder Einschränkungen für gegenseitige Abhängigkeiten festlegen, die Sie in DTD bzw. XML-Schema nicht darstellen können.

Auf dieser Seite:

Siehe auch Validierung und .

Der Wert solcher automatisierter Testverfahren zeigt sich sofort, wenn die Tests wiederholt durchgeführt werden sollen. Stellen Sie sich vor, Sie müßten eine Datei mit nur 100 Detailinformationen durch optische Kontrolle testen. Irgendwo finden Sie einen Fehler; Sie ändern die Implementierungslogik und überprüfen sämtliche Ergebnisse erneut, von Anfang bis Ende. Bei jedem gefundenen Fehler dasselbe Spiel: Korrektur der Programmierung, neue Prüfung. Ich vermute, Sie werden bald die Geduld verlieren und nach einem automatisierten Testverfahren rufen, das Ihnen die lästige und stupide Detailprüfung abnimmt, eines, das "das" von selbst macht.

Schematron als Ergänzung zu DTD / RNG / XML-Schema

Schauen Sie sich das folgende XML-Schema an, das zwar eine generelle Grundstruktur bietet, jedoch keine Aussagen über Datentypen oder gegenseitige Abhängigkeiten trifft.


<xs:schema id="Orte" xmlns:xs="http://www.w3.org/2001/XMLSchema" >
 <xs:element name="Orte">
  <xs:complexType>
   <xs:choice maxOccurs="unbounded">
    <xs:element name="Ort">
     <xs:complexType>
      <xs:choice maxOccurs="unbounded">
       <xs:element name="id" type="xs:string" minOccurs="0" />
       <xs:element name="name" type="xs:string" minOccurs="0" />
       <xs:element name="Mensch" minOccurs="0" maxOccurs="unbounded">
        <xs:complexType>
         <xs:choice maxOccurs="unbounded">
          <xs:element name="id" type="xs:string" minOccurs="0" />
          <xs:element name="name" type="xs:string" minOccurs="0" />
          <xs:element name="vorname" type="xs:string" minOccurs="0" />
          <xs:element name="Gehalt" type="xs:string" minOccurs="0" />
          <xs:element name="idOrt" type="xs:string" minOccurs="0" />          
         </xs:choice>
        </xs:complexType>
       </xs:element>
      </xs:choice>
     </xs:complexType>
    </xs:element>
   </xs:choice>
  </xs:complexType>
 </xs:element>
</xs:schema>

Schematron bietet effiziente Möglichkeiten, zusätzliche Regeln bzw. Einschränkungen zu definieren und deren Einhaltung in einer separaten Prüfung automatisiert zu kontrollieren.

Die folgenden Schematron-Anweisungen sind in der Lage, derlei Vorgaben zu prüfen:


<sch:schema 
  xmlns:sch="http://purl.oclc.org/dsdl/schematron" 
  queryBinding="xslt2">
  <sch:pattern>
    <sch:rule context="/Orte/Ort">
      <sch:assert test="exists(id)">
        ERROR: Ort/id ist nicht vorhanden
      </sch:assert>
      <sch:assert test="id castable as xs:nonNegativeInteger">
        Ort/id ist kein xs:nonNegativeInteger-Typ 
        Fehlerhafter Inhalt: <sch:value-of select="id/text()"/>
      </sch:assert>
      <sch:assert test="count(id)=1">
        ERROR: Ort/id ist ggf. mehrfach oder gar nicht vorhanden
      </sch:assert>
      <sch:assert test="exists(name)">
        ERROR: Ort/name ist nicht vorhanden
      </sch:assert>
      <sch:assert test="count(name)=1">
        ERROR: Ort/name ist ggf. mehrfach oder gar nicht vorhanden
      </sch:assert>
      <sch:assert test="matches(name/text(), '[A-Z]{1}[a-zäöüß]{1,19}')">
        Fehlerhaftes Pattern bei Ort/name Wert: <sch:value-of select="."/>
      </sch:assert>
    </sch:rule>
    <sch:rule context="/Orte/Ort/Mensch">
      <sch:assert test="exists(id)">
        ERROR: Mensch/id ist nicht vorhanden
      </sch:assert>
      <sch:assert test="id castable as xs:nonNegativeInteger">
        Mensch/id ist kein xs:nonNegativeInteger-Typ 
        Fehlerhafter Inhalt: <sch:value-of select="id/text()"/>
      </sch:assert>
      <sch:assert test="count(id)=1">
        ERROR: Mensch/id ist ggf. mehrfach oder gar nicht vorhanden
      </sch:assert>
      <sch:assert test="exists(vorname)">
        ERROR: Mensch/vorname ist nicht vorhanden
      </sch:assert>
      <sch:assert test="matches(vorname/text(), '[A-Z]{1}[a-zäöüß]{1,19}')">
        Fehlerhaftes Pattern bei Mensch/vorname Wert: 
        <sch:value-of select="vorname"/>
      </sch:assert>
      <sch:assert test="count(vorname)=1">
        ERROR: Mensch/vorname ist ggf. mehrfach oder gar nicht vorhanden
      </sch:assert>
      <sch:assert test="exists(name)">
        ERROR: Mensch/name ist nicht vorhanden
      </sch:assert>
      <sch:assert test="matches(name/text(), '[A-Z]{1}[a-zäöüß]{1,19}')">
        Fehlerhaftes Pattern bei Mensch/name Wert: 
        <sch:value-of select="vorname"/>
      </sch:assert>
      <sch:assert test="matches(name/text(), 'los$')">
        Mensch/name entspricht nicht dem Pattern 'los$'.
        real content: <sch:value-of select="name/text()"/>
      </sch:assert>
      <sch:assert test="exists(Gehalt)">
        ERROR: Mensch/Gehalt ist nicht vorhanden
      </sch:assert>
      <sch:assert test="Gehalt &lt; 500" >
        <sch:value-of select="vorname"/> <sch:value-of select="name"/> 
        verdient weniger als 500. Verdienst: <sch:value-of select="Gehalt"/>
      </sch:assert>
      <sch:assert test="Gehalt castable as xs:decimal">
        Gehalt ist kein decimal-Typ 
        real content: <sch:value-of select="Gehalt/text()"/>
      </sch:assert>
      <sch:assert test="../id=current()/idOrt">
        Ort/id <sch:value-of select="../id"/> 
        stimmt nicht ueberein mit Mensch/idOrt.<sch:value-of select="idOrt"/>
      </sch:assert>
    </sch:rule>
  </sch:pattern>
</sch:schema>

Effizient arbeiten mit sch:diagnostics und abstrakten sch:pattern

Da es für Informatiker bekanntlich eine Zumutung darstellt, dieselbe Anweisung zweimal zu schreiben, bietet sich eine Aufteilung mithilfe sch:diagnostics sowie abstrakter sch:pattern an.

Dabei definiert sch:diagnostic eine Meldung, die sich aus dem aktuellen Kontext ergibt und in den eigentlichen Aufruf eingefügt werden kann.

Einem abstrakten sch:pattern können Sie einen Parameter übergeben, der erst zur Laufzeit ausgeführt wird. Damit kann das sch:pattern für unterschiedliche Nodelisten verwendet werden.


<sch:schema 
  xmlns:sch="http://purl.oclc.org/dsdl/schematron" 
  queryBinding="xslt2">
  <sch:diagnostics>
    <sch:diagnostic id="MenschVerdientZuwenig">
      <sch:value-of select="vorname"/> <sch:value-of select="name"/> 
      verdient zuwenig. Verdienst: <sch:value-of select="Gehalt"/>
    </sch:diagnostic> 
    <sch:diagnostic id="FehlerhaftesPattern">
      Fehlerhaftes Pattern; 
      <sch:value-of select="name()"/>[<sch:value-of select="position()"/>]
    </sch:diagnostic>
    <sch:diagnostic id="ElementNichtVorhanden">
      Das Element ist nicht vorhanden: 
      <sch:value-of select="name()"/>[<sch:value-of select="position()"/>]
    </sch:diagnostic>
    <sch:diagnostic id="ElementMehrfachOderGarNichtVorhanden">
      Das Element ist ggf. mehrfach oder gar nicht vorhanden: 
      <sch:value-of select="name()"/>[<sch:value-of select="position()"/>]
    </sch:diagnostic>
    <sch:diagnostic id="id_idref_Fehler">
      /Ort/id <sch:value-of select="../id"/> 
      stimmt nicht ueberein mit Mensch/idOrt 
      <sch:value-of select="idOrt"/>
    </sch:diagnostic>
    <sch:diagnostic id="Typfehler">
      Typfehler
    </sch:diagnostic>
  </sch:diagnostics>
  <sch:pattern abstract="true" id="pattern_id_name">
    <sch:rule context="$p">
      <sch:assert test="exists(id)" 
        diagnostics="ElementNichtVorhanden">
        id: 
      </sch:assert>
      <sch:assert test="id castable as xs:nonNegativeInteger" 
        diagnostics="Typfehler">
        id (<sch:value-of select="id/text()"/>) 
        ist kein xs:nonNegativeInteger-Typ: 
      </sch:assert>
      <sch:assert test="count(id)=1" 
        diagnostics="ElementMehrfachOderGarNichtVorhanden">
        id: 
      </sch:assert>
      <sch:assert test="exists(name)" 
        diagnostics="ElementNichtVorhanden">
        name: 
      </sch:assert>
      <sch:assert test="count(name)=1" 
        diagnostics="ElementMehrfachOderGarNichtVorhanden">
        name: 
      </sch:assert>
      <sch:assert test="matches(name/text(), '[A-Z]{1}[a-zäöüß]{1,19}')" 
        diagnostics="FehlerhaftesPattern">
        name: 
      </sch:assert>
    </sch:rule>
  </sch:pattern>
  <sch:pattern id="pOrt_id_name" is-a="pattern_id_name">
    <sch:param name="p" value="/Orte/Ort"/>    
  </sch:pattern>
  <sch:pattern id="pMensch_id_name" is-a="pattern_id_name">    
    <sch:param name="p" value="/Orte/Ort/Mensch"/>
  </sch:pattern>
  <sch:pattern>    
    <sch:rule context="/Orte/Ort/Mensch">
      <sch:assert test="exists(vorname)" 
        diagnostics="ElementNichtVorhanden">
        Mensch/vorname: </sch:assert>
      <sch:assert test="matches(vorname/text(), '[A-Z]{1}[a-zäöüß]{1,19}')" 
        diagnostics="FehlerhaftesPattern">
        Mensch/vorname: 
      </sch:assert>
      <sch:assert test="exists(Gehalt)" 
        diagnostics="ElementNichtVorhanden">
        Mensch/Gehalt: 
      </sch:assert>
      <sch:assert test="Gehalt castable as xs:decimal" 
        diagnostics="Typfehler">
        Gehalt (<sch:value-of select="Gehalt"/>) ist kein xs:decimal-Typ:
      </sch:assert>
      <sch:assert test="Gehalt castable as xs:decimal and Gehalt &lt; 500" 
        diagnostics="MenschVerdientZuwenig"/>
      <!--Deklaration zweier Variablen-->
      <sch:let name="vidort" value="current()/idOrt"/>
      <sch:let name="vparentid" value="../id"/>      
      <sch:assert test="$vparentid = $vidort" 
        diagnostics="id_idref_Fehler"/>
    </sch:rule>
  </sch:pattern>
</sch:schema>

Detailtests mit Schematron

Sie können die -Pfade auch sehr präzise auf einzelne Elemente setzen und deren Textinhalte prüfen.


<sch:schema queryBinding="xslt2" 
   xmlns:sch="http://purl.oclc.org/dsdl/schematron">
 <sch:pattern>
    <sch:rule context="/">
      <sch:assert test="exists(Orte)">
        ERROR: Root-Element Orte DOES NOT EXIST</sch:assert>
      <sch:assert test="exists(Orte/Ort[1])">
        ERROR: Orte/Ort[1] DOES NOT EXIST</sch:assert>
      <!-- stellt sicher, dass es kein Orte/Ort[2] gibt -->
      <sch:assert test="not(exists(Orte/Ort[2]))">
        ERROR: Ort[2] EXISTS: more than one occurance of /Orte/Ort
      </sch:assert>
    </sch:rule>
    <!-- stellt sicher, dass der Wert in 
    /Orte/Ort[3]/Mensch[2]/name[1] 'Sinnlos' ist -->
    <sch:rule context="/Orte/Ort[2]/Mensch[3]/name[1]">
      <sch:assert test="./text()='Sinnlos'">
        WRONG VALUE IN /Orte/Ort[1]/Mensch[5]/name[1]; 
        expected value: Sinnlos | 
        real content: <sch:value-of select="./text()"/>
      </sch:assert>
      <sch:assert test="./text()!='SINNLOS'">
        WRONG VALUE IN /Orte/Ort[2]/Mensch[3]/name[1]; 
        value must not be: SINNLOS | 
        real content: <sch:value-of select="./text()"/>
      </sch:assert>
    </sch:rule>
  </sch:pattern>
</sch:schema>

Automatisiertes Erzeugen von Schematron-Dateien

Nun ist die Generierung von Schematron-Dateien für komplexe Ergebnisdokumente durchaus mit einigem Aufwand verbunden. Eine beträchtliche Hilfe kann darin bestehen, das erwartete XML-Dokument (das nach Durchlaufen des gesamten XSLT-Prozesses generiert werden soll) (ggf. nach Korrektur) zu fixieren und auf dieser Basis die Schematron-Datei weitgehend automatisch zu generieren. Dabei kann folgendes XSL-Stylesheet hilfreich sein (Die Logik generiert Schematron-Dateien für Elemente mit Textinhalt; für die Überprüfung von Attributen wären zusätzliche Schritte erforderlich):


<xsl:stylesheet 
     version="1.0" 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/"> 
    <sch:schema queryBinding="xslt2"
      xmlns:sqf="http://www.schematron-quickfix.com/validator/process"
      xmlns:sch="http://purl.oclc.org/dsdl/schematron"> 
      <sch:pattern> 
        <xsl:for-each select="/*/descendant-or-self::*
                                 [count(child::*) =0]/parent::*">          
          <xsl:variable name="vxpath">
            <xsl:text>/</xsl:text>
            <xsl:for-each select="ancestor-or-self::*">
              <xsl:value-of select="name()"/>
              <xsl:if test="../*[2]">
                <xsl:text>[</xsl:text>
                <xsl:value-of select="1+count(
                     preceding-sibling::*[name(.)=name(current())])"/>
                <xsl:text>]</xsl:text>
              </xsl:if>
              <xsl:choose>
                <xsl:when test="position() != last()">
                  <xsl:text>/</xsl:text>
                </xsl:when>
              </xsl:choose>
            </xsl:for-each>
          </xsl:variable>  
          <sch:rule>  
            <xsl:attribute name="context">
              <xsl:value-of select="$vxpath"/>
            </xsl:attribute> 
            <xsl:for-each select="child::*[count(child::*) =0]">
              <xsl:variable name="vexpectedvalue">
                <xsl:value-of select="./text()"/>
              </xsl:variable>
              <sch:assert>
                <xsl:attribute name="test">                  
                  <xsl:text>exists(</xsl:text>
                  <xsl:value-of select="name()"/>
                  <xsl:text>)</xsl:text>
                </xsl:attribute>
                <xsl:value-of select="$vxpath"/>
                <xsl:text>/</xsl:text>
                <xsl:value-of select="name()"/>
                <xsl:text> DOES NOT EXIST</xsl:text>
              </sch:assert>              
              <sch:assert>
                <xsl:attribute name="test">
                  <xsl:value-of select="name()"/>
                  <xsl:text>/text()='</xsl:text>
                  <xsl:value-of select="./text()"/>
                  <xsl:text>'</xsl:text>
                </xsl:attribute>
                <xsl:text> WRONG VALUE IN </xsl:text>
                <xsl:value-of select="$vxpath"/>
                <xsl:text>/</xsl:text>
                <xsl:value-of select="name()"/>
                <xsl:text> Expected value: </xsl:text>
                <xsl:value-of select="$vexpectedvalue"/>                
                <xsl:text> | real content: </xsl:text>  
                <sch:value-of select="./text()"/>
              </sch:assert>  
            </xsl:for-each>            
          </sch:rule> 
        </xsl:for-each> 
      </sch:pattern>
    </sch:schema>
  </xsl:template>
</xsl:stylesheet>

Neben zahlreichen anderen klar adressierbaren -Statements und deren erwartetem Inhalt wird damit folgender Output generiert (stark gekürzte Darstellung), der anschließend in einem geeigneten Schematron-Validierungstool (basierend auf dem realen XSL-Ergebnis) aufgerufen wird; damit werden Abweichungen zwischen SOLL und IST schnell reportet.


<sch:schema 
   xmlns:sch="http://purl.oclc.org/dsdl/schematron"
   queryBinding="xslt2">
  <sch:pattern>
   <sch:rule 
      context="/Orte/Ort[1]/Mensch[1]/Kauf[1]">
     <sch:assert 
       test="exists(idMensch)">
       /Orte/Ort[1]/Mensch[1]/Kauf[1]/idMensch DOES NOT EXIST
     </sch:assert>
     <sch:assert 
       test="idMensch/text()='1'"> 
       WRONG VALUE IN /Orte/Ort[1]/Mensch[1]/Kauf[1]/idMensch 
       Expected value: 1 | real content: 
       <sch:value-of select="./text()"/>
     </sch:assert>
   </sch:rule>
  </sch:pattern>
</sch:schema>

wg / 7. Juni 2020



Fragen? Anmerkungen? Tipps?

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/Schematron.html