XSL - Übersicht / xsl:import-schema

xsl:import-schema

xsl:import-schema

xsl:import-schema bindet XML Schema-Validierung in den XSL-2.0-Transformationsprozeß ein.

xsl:import-schema

Das setzt voraus, daß der verwendete auch "schema-aware" ist.

In diesem Abschnitt wird das Mapping von einer sequenziellen XML Tabellenstruktur in eine temp. Variable besprochen, von der aus sich weitere Folgemappings in die unterschiedlichsten Zielstrukturen aufrufen lassen.

Der Unterschied zur vorhergehenden Aufgabenstellung liegt darin, daß wir es nunmehr mit einer komplett sequenziellen XML Struktur zu tun haben. Führende und schließende Tags, die Ort-, Mensch- oder Kauf-Elemente strukturieren würden, gibt es nicht: daher ist auch eine Abarbeitung eines klaren XPath-Statements nicht möglich. Um dieses zu erreichen, geht es hier darum, die sequenziell strukturierten Abhängigkeiten über ein XSL Stylesheet aufzubauen, um anschließend typsicher und komfortabel weiter arbeiten zu können.

Recht einfach ist hier noch der Import des XML Schemas, mit dem auch hier die generierte temp. Variable validiert werden soll.

Da "xsl:import-schema" nur von einem XSL-Prozessor ausgeführt werden kann, der auch Schema-aware ist, empfiehlt sich hier der Einschub "use-when="system-property('xsl:is-schema-aware') eq 'yes'".


<xsl:import-schema 
  use-when="system-property('xsl:is-schema-aware') eq 'yes'
  schema-location="../xsd/tempTreeOrte.xsd"/>

Dieses externe Schema tempTreeOrte.xsd hat folgenden Aufbau:


<xs:schema 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    elementFormDefault="qualified">
    <xs:simpleType name="myStringMax20">
        <xs:annotation>
            <xs:documentation>definiert einen 20 Chars 
              langen String, dessen erstes Zeichen [A-Z] 
              sein muss; die restlichen Zeichen 
              (mind. 1, max. 19) sind [a-z].
            </xs:documentation>
        </xs:annotation>
        <xs:restriction base="xs:string">
            <xs:pattern value="[A-Z][a-z]{1,19}"/>
        </xs:restriction>
    </xs:simpleType>
    <xs:element name="Orte">
        <xs:annotation>
            <xs:documentation>
               Rooteement für den temp. Baum
            </xs:documentation>
        </xs:annotation>
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="Ort" maxOccurs="unbounded" minOccurs="0"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="Ort">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="id"/>
                <xs:element ref="name"/>
                <xs:element maxOccurs="unbounded" minOccurs="0" ref="Mensch"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="Mensch">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="id"/>
                <xs:element ref="name"/>
                <xs:element ref="vorname"/>
                <xs:element ref="Gehalt"/>
                <xs:element maxOccurs="unbounded" minOccurs="0" ref="Kauf"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="Kauf">
        <xs:complexType>
            <xs:sequence>
                <xs:element ref="bez"/>
                <xs:element ref="Gesamt"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
    <xs:element name="bez" type="myStringMax20"/>
    <xs:element name="Gesamt" type="xs:decimal"/>
    <xs:element name="id" type="xs:nonNegativeInteger"/>
    <xs:element name="name" type="myStringMax20"/>
    <xs:element name="vorname" type="myStringMax20"/>
    <xs:element name="Gehalt" type="xs:decimal"/>
</xs:schema>

Das folgende Template ist nicht über einen eigenständigen Namen definiert, sondern arbeitet sämtliche Elemente ab, die den Namen "id" tragen. Dabei wird geprüft, ob das jeweils übernächste Element ebenfalls den Namen "id" hat. Falls ja, wird ein Ort-Element generiert mit seinen Childnodes id und name. Falls nein, so wird überprüft, ob der übernächste Elementname "vorname" ist; dann wird ein Element "Mensch" generiert mit seinen Childnodes id, name, vorname, Gehalt und idOrt.


 <xsl:template match="id">
  <xsl:choose>
   <xsl:when 
    test="following-sibling::*[position() = 2]/local-name()='id'">
    <Ort>
     <id>
      <xsl:value-of select="."/>
     </id>
     <name>
      <xsl:value-of select="following-sibling::*[1]"/>
     </name>
    </Ort>
   </xsl:when>
   <xsl:when 
        test="following-sibling::*[2]/local-name()='vorname'">
    <Mensch>
     <id><xsl:value-of select="."/></id>
     <xsl:for-each 
      select="following-sibling::*[position() &amp;lt; 5]">
      <xsl:copy-of select="."/>
     </xsl:for-each>
    </Mensch>
   </xsl:when>
  </xsl:choose>
 </xsl:template>

Ähnlich verfährt auch das Template, das sich um alle Elementnamen "idMensch" kümmert: für jeden Fall wird ein Kauf-Element generiert und mit den Childnodes idMensch, bez und Gesamt ergänzt. Dabei wird auch hier konsequent mit einem Type-Casting der atomaren Werte gearbeitet. Das erlaubt, auch Werte korrigieren, die im XML Input ggf. schon fehlerhaft vorliegen. Damit werden Ergebnisse wie <Gesamt>142.89</Gesamt> möglich, die andernfalls so ausgesehen hhätten: <Gesamt>142.89000000000002</Gesamt>


 <xsl:template match="idMensch">
  <Kauf>
   <idMensch>
     <xsl:value-of select="."/>
   </idMensch>
   <bez>
     <xsl:value-of select="following-sibling::*[2]"/>
   </bez>    
   <xsl:variable name="vanzahl" 
     select="xs:decimal(following-sibling::*[1])" 
     as="xs:decimal"/>
   <xsl:variable name="vpreis" 
     select="xs:decimal(following-sibling::*[3])" 
     as="xs:decimal"/>
   <Gesamt>
     <xsl:value-of select="$vanzahl * $vpreis"/>
   </Gesamt>
  </Kauf>
 </xsl:template>

Aufgerufen werden diese Template-matches durch eine zusätzliche Variable vListe, die zunächst nichts Anderes beinhaltet als eine Abfolge von

a) einzelnen Ort-Elementen mit den Childnodes id und name,

b) einzelnen Mensch-Elementen mit den Childnodes id, name, vorname, Gehalt und idOrt, und

c) einzelnen Kauf-Nodes mit deren Childnodes idMensch, bez und Gesamt.


 <xsl:variable name="vListe">
  <xsl:apply-templates select="//id"/>
  <xsl:apply-templates select="//idMensch"/>
 </xsl:variable>

Im folgenden Schritt (vOrte_from_seq) erfolgt noch die Zuordnung über Schlüsselbeziehungen, die über Variablen abgesichert werden. Hier wird der eigentliche temporäre Baum erzeugt, der auch mit dem XML Schema abgeglichen werden kann.


 <xsl:variable name="vOrte_from_seq" as="item()">
  <Orte xsl:validation="strict">
   <xsl:for-each select="$vListe/Ort">
    <Ort>
     <xsl:copy-of select="id"/>
     <xsl:copy-of select="name"/>
     <xsl:variable name="vid" select="id"/>
     <xsl:for-each select="$vListe/Mensch[idOrt = $vid]">
      <xsl:variable name="vidmensch" select="id"/>
      <Mensch>
       <xsl:for-each 
        select="child::*[local-name() != 'idOrt']">
        <xsl:copy-of select="."/>
       </xsl:for-each>       
       <xsl:for-each 
        select="$vListe/Kauf[idMensch = $vidmensch]">
        <Kauf>         
         <xsl:for-each
          select="child::*[local-name() = ('bez', 'Gesamt')]">
          <xsl:copy-of select="."/>
         </xsl:for-each>
        </Kauf>
       </xsl:for-each>
      </Mensch>
     </xsl:for-each>
    </Ort>
   </xsl:for-each>
  </Orte>
 </xsl:variable>

Arbeiten mit xsl:import-schema

Die XML Schema-Validierung kann auch im Rahmen der XSL-Transformation durchgeführt werden. Zudem kann im XSL-Stylesheet ein XML Schema definiert werden. Wie schon anderswo gesagt: da "xsl:import-schema" nur von einem XSL-Prozessor ausgeführt werden kann, der auch Schema-aware ist, empfiehlt sich hier der Einschub "use-when="system-property('xsl:is-schema-aware') eq 'yes'".


 <xsl:import-schema 
  use-when="system-property('xsl:is-schema-aware') eq 'yes'>
  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <xs:element name="wurzel">
    <xs:complexType>
     <xs:sequence>
      <xs:element name="koordinate" maxOccurs="unbounded">
       <xs:complexType>
        <xs:attribute name="X" type="xs:integer" />
        <xs:attribute name="Y" type="xs:integer" />
       </xs:complexType>
      </xs:element>
     </xs:sequence>
    </xs:complexType>
   </xs:element>
  </xs:schema>
 </xsl:import-schema>
 <xsl:variable name="vKoordinate">
  <wurzel xsl:validation="strict">
   <xsl:for-each select="1 to 19">
    <koordinate X="{.}" y="{.}" />
   </xsl:for-each>
  </wurzel>
 </xsl:variable>
 <xsl:template name="validationdemo">
  <xsl:copy-of select="$vKoordinate" />
 </xsl:template>

wg / 30. Dezember 2017



Fragen? Anmerkungen? Tips?

Bitte nehmen Sie Kontakt zu mir auf:

Vorname
Nachname
Mailadresse







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: info2018@wilfried-grupe.de

www.wilfried-grupe.de/XSL_import_schema.html