4.1.1  Vortransformationen

Bei einer komplexen Transformation ist es ratsam und sogar manchmal unabdingbar die Konvertierung in einzelne Stufen aufzuteilen. Das hat folgende Vorteile:
  • Der Prozess ist transparenter, da die einzelnen Stufen leichter überschaubar sind.
  • Die Zwischenergebnisse können für Debug-Zwecke ausgewertet werde oder dienen als Eingabe für andere Prozesse.
    | | Rejected by , .
  • Nicht-relevante oder invalide Teilbäume können aus der Eingabeinstanz gefiltert werden, um so die weitere Verarbeitung zu beschleunigen.
  • Hilfskonstrukte können erzeugt werden. Diese erleichtern die weitere Verarbeitung.
Es gibt zwei Möglichkeiten, wie eine Vortransformation eingebunden werden kann:
  • In einem separaten File bzw. einer XML Instanz, die vom XSLT Prozessor vor der eigentlichen Transformation aufgerufen wird und einen Zwischenstand produziert. Dieser kann dann als Eingabe für den Haupttransformationsschritt dienen.
  • Innerhalb des eigentlichen XSLT Stylesheets. Hier wird das Ergebnis der Vortransformation in einer Variablen erzeugt.
Den zweiten Punkt möchte ich anhand eines Beispiel XSLT Skripts vorführen. Betrachten wir folgende Input Daten:
<education-system>
  <administrative-regions>
    [...]
    <dministrative-region id="31" name="Bavaria">
      <shools>
        <school id="45">
          <teachers>
            <teacher id="576"/>
            <teacher id="345"/>
            <teacher id="12"/>
          </teachers>
        </school>
        <school id="36">
          <teachers>
            <teacher id="576"/>
            <teacher id="8"/>
          </teachers>
        </school>
        [...]
      </shools>
    </dministrative-region>
    [...]
  </administrative-regions>
</education-system>
Die erste Datei beinhaltet eine Zuordnung von Lehrern zu Schulen in verschiedenen Regierungsbezirken. Um die Daten zu den beiden referenzierten Objekten einzusehen, müssen zwei weitere Dateien konsultiert werden. Die Datei, welche die Lehrer auflistet:
<teachers>
  [...]
  <teacher id="576">
    <first-name>Alfons</first-name>
    <last-name>Blimetsrieder</last-name>
    <subjects>
      <subject>Biology</subject>
      <subject>Math</subject>
      <subject>Sport</subject>
    </subjects>
    <suspended>2017-12-31</suspended>
    [...]
  </teacher>
  [...]
</teachers>
Und die Datei, welche die Schulen auflistet:
<schools>
  [...]
  <school id="45">
    <name>Gymnasium Bad Aibling</name>
    <type>Oberschule</type>
    [...]
  </school>
  [...]
</schools>
Um diese Daten verarbeiten zu können ist es sinnvoll, die drei Dateien in einem ersten "Resolver" Schritt zusammenzuführen und ggf. irrelevante Strukturen zu entfernen. Lehrer aus obigem Beispiel können beispielsweise suspendiert worden sein. Das folgende Skript erledigt dies mittels einer zusätzlichen Transformation in eine Variable:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  exclude-result-prefixes="#all">
  
  <xsl:output indent="yes" method="xml"/>
  
  <xsl:strip-space elements="*"/>
  
  <xsl:param name="file-1" required="yes"/>
  <xsl:param name="file-2" required="yes"/>
  <xsl:param name="file-3" required="yes"/>
  
  <xsl:variable name="files" select="(doc($file-1), doc($file-2), doc($file-3))"/>
  <xsl:variable name="bavaria-region-ids" select="(31, 58)"/>
  
  <xsl:key name="teachers" match="teacher" use="@id"/>
  <xsl:key name="schools" match="school" use="@id"/>
  
  <xsl:template name="main">
    <xsl:variable name="resolve-result">
      <xsl:apply-templates select="$files/administrative-regions" mode="resolve"/>
    </xsl:variable>
    <xsl:apply-templates select="$resolve-result/administrative-regions"/>
  </xsl:template>
  
  <xsl:template match="administrative-region[not(@id = $bavaria-region-ids)]" 
                mode="resolve"/>

  <xsl:template match="school" mode="resolve">
    <xsl:copy>
      <xsl:copy-of select="key('schools',@id, $files/schools[1]/root())/node()"/>
      <xsl:apply-templates select="node()|@*" mode="resolve"/>      
    </xsl:copy>
  </xsl:template>

  <xsl:template match="teacher" mode="resolve">
    <xsl:copy-of select="key('teachers',@id, $files/teachers[1]/root())/node()"/>
  </xsl:template>
    
  <xsl:template match="teacher[suspended/xs:date(.) le current-date()]"/>
  
  <xsl:template match="node()|@*" mode="#all">
    <xsl:copy>
      <xsl:apply-templates mode="#current"/>
    </xsl:copy>
  </xsl:template>
  
</xsl:stylesheet>
Im ersten Resolve-Schritt werden die Referenzen zu den Lehrer- und Schul-Objekten aufgelöst, d.h. die Attribute des Schul-Objekts werden in die Struktur aus der ersten Datei kopiert.
Die Liste der Lehrer an diesen Schul-Objekten bleibt erhalten und wird mit dem Inhalt aus der zweiten Datei bestückt.
Zusätzlich werden alle Regierungsbezirke entfernt, die nicht zu Bayern gehören - was die weitere Verarbeitung wesentlich beschleunigen wird. Lehrer die suspendiert worden sind fliegen ebenfalls raus ...

4.1.1.1  In-Situ Vortransformation

Als ich mir kürzlich meinen Code vor über zehn Jahren zu Gemüte führte, fiel mir ein augenscheinlich sehr seltsames Stück XSLT auf, sinngemäss:
Datei: common/semantic-tables.xsl

<xsl:template name="maintenance-table">
    <cals-table-structure>
        [...]
            <<xsl:apply-templates select="maint-int"/>
        [...]
    </cals-table-structure>
</xsl:template>

<xsl:template match="maintenance-table">
    <xsl:variable name="maintenance-table">
        <xsl:call-template name="maintenance-table"/>
    </xsl:variable>
    <xsl:apply-templates select="$maintenance-table"/>
</xsl:template>
Was hat mich denn da geritten? Nach einer kurzen Zeit des in mich Gehens, bin ich dann schon darauf gekommen. Der Clou ist hier eine Vortransformation innerhalb einer Match-Regel.
Die Transformation innerhalb der Variablen rendert die semantischen Elemente der Wartungstablle, wie z.B. das Wartungsintervall maint-int , in eine CALS-Tabelle.
Diese wird dann im nächsten Transformationsschritt entweder nach XSL-FO oder nach HTML transformiert, je nachdem welche Haupt-Datei main.xsl das Modul semantic-tables.xsl importiert.
Datei html/main.xsl

<xsl:import href="common/semantic-tables.xsl"/>

<xsl:template match="cals-table-structure">
    <html-tabllen-struktur>
        [...]
    </html-tabllen-struktur>
</xsl:template>

Datei pdf/main.xsl

<xsl:import href="common/semantic-tables.xsl"/>

<xsl:template match="cals-table-structure">
    <pdf-tabllen-struktur>
        [...]
    </pdf-tabllen-struktur>
</xsl:template>
Dieser Ansatz ist sehr flexibel, denn im herausfaktorisierten Tabellenalgorithmus können leicht Sonderfälle, wie Duplikat-Eliminierung oder spezielle Merge-Operation, abgefangen werden. Zudem können sowohl das Named-Template bzgl. der Wartungstabelle als auch die Match-Regel im importierenden Stylesheet überschrieben werden, was der Kreativität keine Grenzen setzt.

4.1.1.2  Mehrstufige Transformationen

Bei manchen Kovertierungen reichen ein oder zwei hintereinander geschaltete Transformationsschritte nicht aus.
Vielleicht hat man es mit einer Struktur zu tun, die gar nicht zum Zielformat passt... Sei es, weil auf ein sehr restriktives Inhaltsmodel transformiert wird, sei es weil ERROR! Linktarget does not exist erst schrittweise erschlossen wird, da der Überblick über die Daten noch fehlt.
In jedem Fall muss für manche Elemente in einem späteren Schritt entschieden werden, wohin sie umsortiert werden sollten, weil sie jetzt gerade stören.
Beispiele:
1.

Transformationen auf ein restriktiveres Inhaltsmodell

  • Das XHTML eines Webeditors, bei dem der User willkürlich die unterschiedlichsten Strukturen eingeben kann, wird auf die restrikte DTD eines XML Redaktionssystems gemappt.
2.

Ein restriktives, strukturiertes Inhaltsmodell soll in ein freieres, strukturiertes Inhaltsmodell konvertiert werden:

  • Diese Form der Transformation findet man oft bei den Ausgabestrecken eines XML Redaktionssystems. Hier kommt man gut mit 2-3 Transformationsschritten aus. Diese werden dazu verwendet, um die Konvertierung zu erleichtern, und nicht aus "Platzgründen" wie in Punkt 1.)
3.

Transformationen von relativ unstrukturierten Daten. Hier muss man die Strukturen erst bilden. Das kann durch Einsammeln gleichartiger Elemente passieren, die dann hierarchisch in einer Baumstruktur geordnet werden.

  • Baumstrukturen kann man z.B. auch aus Dateipfaden generieren, die dann mittels anderer Datenquellen noch angereichert werden, bspw. CSV-Dateien.
Mehrstufige Transformationen werden in Migrationen eingesetzt.
Previous Page Next Page
Version: 93
Jan 25 2021