- 1 Intro
- 2 Anwendungsgebiete
- 2.1 XSLT - die Programmiersprache im XML Bereich
- 2.2 Aktuelle und vergangene Anwendungen
- 2.3 Professionelle XML Verarbeitung
- 2.4 Technische Dokumentation
- 3 Wichtige Konzepte
- 3.1 Push vs. Pull Stylesheets
- 3.2 Eindeutigkeit der Regelbasis
- 3.3 Namespaces
- 3.4 Schemata
- 3.5 Standards
- 3.5.1 DITA
- 3.5.2 DITA Inhaltsmodell
- 3.5.1 DITA
- 4 Ausgewählte Themen
- 4.1 Transformationen mit XSLT
- 4.1.1 Vortransformationen
- 4.1.2 Komplexe XML-2-XML Transformationen
- 4.1.2.8 Vererbung
- 4.1.2.8 Vererbung
- 4.1.3 XSLT Streaming
- 4.1.3.1 XSLT Akkumulator
- 4.1.3.2 XSLT Iterator
- 4.1.4 Reguläre Ausdrücke
- 4.1.5 Modus vs. Tunnel Lösung
- 4.1.6 Identifikation mit
generate-id()
- 4.1.6.4 XPath-Achsenbereich selektieren
- 4.1.6.4.1 Funktionen und Module
- 4.1.6.4.1 Funktionen und Module
- 4.1.6.4 XPath-Achsenbereich selektieren
- 4.1.7 Webservice Calls mit doc() und unparsed-text()
- 4.1.8 Stylesheet-Parameter auf der Kommandozeile
- 4.1.9 Leerzeichenbehandlung
- 4.1.10 Mit
translate
Zeichen ersetzen
- 4.1.10.1 Spass mit dem Sequenzvergleich
- 4.1.11 Character Mappings in der Ausgabe
- 4.1.12 JSON mit XSLT 1.0 und Python lxml
- 4.1.1 Vortransformationen
- 4.2 Abfragen mit XQuery
- 4.2.5 XQuery als Programmiersprache
- 4.2.5.3
if..then..else
Ausdrücke
- 4.2.5.3.2 SQL Views in MarkLogic
- 4.2.5.3
if..then..else
Ausdrücke
- 4.2.6 Hilfreiche XQuery Schippsel
- 4.2.5 XQuery als Programmiersprache
- 4.3 XML Datenbanken
- 4.3.1 Connector zu Marklogic in Oxygen
- 4.3.2 Bi-Temporale Dokumente
- 4.3.2.1 Anlegen des Testszenarios auf der ML Konsole
- 4.3.2.2 Ausführen einiger Beispiel-Queries
- 4.3.3 Webapps mit MarkLogic
- 4.3.3.5 Wikipedia Scrapper Applikation
- 4.3.3.5 Wikipedia Scrapper Applikation
- 4.3.4 Dokument-Rechte in MarkLogic
- 4.3.5 MarkLogic Tools
- 4.3.5.1 EXPath Konsole
- 4.3.5.2 mlcp - MarkLogic Content Pump
- 4.3.5.3 Deployment-Tools
- 4.4 XSL-FO mit XSLT1.x
- 4.5 Testing
- 4.5.1 Validierung mit Schematron
- 4.5.2 Erste Schritte mit XSpec
- 4.5.1 Validierung mit Schematron
- 4.6 Performanz-Optimierung
- 4.1 Transformationen mit XSLT
- 5 Zusätzliches Know-How
- 5.1 XML Editoren
- 5.2 Quellcode-Versionskontrolle
- 5.2.1 Kurze Geschichte zur Versionskontrolle Test
- 5.2.2 GIT Kommandos
- 5.2.1 Kurze Geschichte zur Versionskontrolle Test
- 5.1 XML Editoren
- 6 Glossary
- 7 Tektur CCMS
4.1.3.2 XSLT Iterator
Betrachten wir folgendes Problem. Es soll ein kommaseparierter Report aus dieser XML Quelle generiert werden.
<status-report> <status-change> <billing_id>360788</billing_id> <claim_ids>967382,673647</claim_ids> <status>open</status> <time_stamp>2019-02-22T13:53:34.605Z</status_time> </status-change> <status-change> <billing_id>360788</billing_id> <claim_ids>967382,673647</claim_ids> <status>open</status> <time_stamp>2019-02-22T13:53:34.605Z</status_time> </status-change> [...]
Mit einer
for-each
Loop und einem Named-Template würde das so gehen:
<xsl:template name="main"> <xsl:for-each select="$input-file/status-report/status-change"> <xsl:value-of select="concat(billing_id,',')"/> <xsl:value-of select="concat(claim_ids,',')"/> <xsl:value-of select="concat(status,',')"/> <xsl:value-of select="concat(format-dateTime(xs:dateTime(time_stamp), '[Y]-[M]-[D] [H]:[m]'),' ')"/> </xsl:for-each> </xsl:template>
Named-Templates, die direkt über den Saxon Aufruf
saxon -it:main
aufgerufen werden, sind dann
brauchbar, wenn keine eindeutige Eingabequelle vorhanden ist, bspw. weil aus mehreren
Quellen eingelesen werden soll, falls die Eingabe von einem Webservice kommt oder
vom XSLT Skript selbst erzeugt wird.
Diesen Code kann man vereinfachen: Da von einer Datei eingelesen wird, brauchen wir kein Named-Template. Statt der Schleife können wir uns auch auf den rekursiven Abstieg des XSLT Prozessors
verlassen:
<xsl:template match="/status-report/status-change"> <xsl:value-of select="concat(billing_id,',')"/> <xsl:value-of select="concat(claim_ids,',')"/> <xsl:value-of select="concat(status,',')"/> <xsl:value-of select="concat(format-dateTime(xs:dateTime(time_stamp), '[Y]-[M]-[D] [H]:[m]'),' ')"/> </xsl:template>
Wollen wir große Datenmengen schnell verarbeiten - mit ein paar Hundert MB, so ist es sinnvoll
auf die neue XSLT3.0 Streaming Option umzuschalten, weil dadurch kein Eingabebaum in-Memory
aufgebaut wird. Wie schon im Kapitel XSLT Akkumulator angesprochen, gibt es dazu mehrere Möglichkeiten.
Wir betrachten hier das
xsl:iterator
(Doku) ↗
Konstrukt und stossen dabei auf einige Fallstricke. Zunächst zu den
Settings:
-
Wir benutzen xsl:source-document in Verbindung mit dem streamable='yes' Attribut, um dem Prozessor mitzuteilen, dass er im Streaming Modus arbeiten soll.
-
Wenn wir die Quelle über einen Parameter einlesen, dann müssen wir auch die Transformation über ein Named-Template starten.
Ohne zu wissen, wie XSLT Streaming genau funktioniert, setzen wir probeweise eine Reihe von
value-of select
statements in den Iterator:
<xsl:template name="main"> <xsl:source-document href="{$input-file}" streamable='yes'> <xsl:iterate select="status-report/status-change"> <xsl:value-of select="concat(billing_id,',')"/> <xsl:value-of select="concat(claim_ids,',')"/> <xsl:value-of select="concat(status,',')"/> <xsl:value-of select="concat(format-dateTime(xs:dateTime(time_stamp), '[Y]-[M]-[D] [H]:[m]'),' ')"/> </xsl:iterate> </xsl:source-document> </xsl:template>
und werden dafür prompt mit einer Fehlermeldung belohnt:
Static error on line 16 column 64 of report.xsl: XTSE3430: The body of the xsl:stream instruction is not streamable * There is more than one consuming operand: {xsl:value-of} on line 18, and {xsl:value-of} on line 19
In diesem Iterator ist also nur eine "konsumierende"
value-of
Operation erlaubt. Um nur einmal zu selektieren, müssen wir - auf Kosten der Lesbarkeit - ziemlich umbauen. Eine Lösung könnte z.B. so aussehen:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" xpath-default-namespace="https://tekturcms.de/schema/status-report/1.0" version="3.0"> <xsl:param name="input-file" required="yes"/> <xsl:output method="text"/> <!-- https://www.saxonica.com/html/documentation/xsl-elements/iterate.html --> <xsl:template name="main"> <xsl:source-document href="{$input-file}" streamable='yes'> <xsl:iterate select="status-report/status-change/*"> <xsl:choose> <xsl:when test="name()='time_stamp'"> <xsl:value-of select="concat(format-dateTime(xs:dateTime(time_stamp), '[Y]-[M]-[D] [H]:[m]'),' ')"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat(.,',')"/> </xsl:otherwise> </xsl:choose> </xsl:iterate> </xsl:source-document> </xsl:template> </xsl:stylesheet>
Hier wird davon ausgegangen, dass das Element mit Namen 'time_stamp' als letztes in der Sequenz vorkommt und beim Auftreten (
) wird ein Zeilenumbruch gesetzt. Der deklarative Ansatz aus dem ersten Beispiel geht dabei verloren.
Logisch gesehen wird beim XSLT Streaming auf einer niedrigeren Abstraktionsebene programmiert, um den Anforderungen des Prozessors gerecht zu werden.
Für eine 1.6 GB Datei benötigt das obige Skript auf meinem Rechner gute drei Minuten. Der traditionelle template-match Ansatz bricht mit einer Out-of-Memory Exception ab, selbst wenn man den Java Heap Size auf 4GB einstellt.