- 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.6 Identifikation mit generate-id()
Die
generate-id()
Funktion gibt es seit XSLT 1.0. Mit ihr kann eine Prüfsumme eines Knotens im Baum generiert werden.
Das funktioniert natürlich nur, wenn man bei der Auswertung dieses Wertes nicht den Kontext wechselt. D.h. z.B. dass ein Knoten in einem Baum,
der in einer Variablen gespeichert ist, eine andere Prüfsumme bekommt, als derselbe Knoten im Kontext-Baum.
4.1.6.1 Beispiel Stückliste
Ein Anwendungszenario wäre bspw. die Generierung einer Target-ID für ein Bauteil in einer Stückliste. Das Bauteil ist nur einmal im
System erfasst, hat also eine eindeutige ID, soll aber an mehreren Stellen in die Ausgabe (Eine Dokumentation für eine Maschine)
generiert werden.
Die Id an einem Element
<part id=“1234”>
würde somit mehrfach in die XML Eingabe für einen XSL-FO Prozessor erscheinen und ist
für Referenzen unbrauchbar geworden. Deshalb ist es ratsam beim Rendern der Bauteile eine neue Id zu vergeben, das kann z.B.
mit den folgenden Templates (vereinfacht) passieren:
<xsl:key name="parts" match="part" use="@id"/> <xsl:template match=“part” mode=“content"> <!-- Ausgabe des Bauteils im Content Bereich --> <fo:block id="{generate-id()}"> <fo:external-graphic xsl:use-attribute-sets="part.img"/> </fo:block> </xsl:template> <xsl:template match=“part” mode=“part-list"> <!-- Ausgabe einer Liste mit allen Verweisen an unterschiedicher Stelle --> <fo:block> <xsl:for-each select="key('parts',@id)"> <fo:page-number-citation ref-id="{generate-id()}"/> </xsl:for-each> </fo:block> </xsl:template>
4.1.6.2 Beispiel Mantel Dokument
Im Bereich EDI Datenaustausch werden große XML Dateien versendet, die man auf einzelne Transmissions
aufsplitten will, um sie in einer XML Datenbank abspeichern zu können. Die Struktur einer Datenübertragung
könnte folgendermassen aussehen:
WRAPPER1 SEQUENZ1 SEQUENZ2 SEQUENZ3 WRAPPER2 SEQUENZ1 SEQUENZ2 SEQUENZ3 SEQUENZ4 WRAPPER3 SEQUENZ1 SEQUENZ2 CONTENT DATA1 DATA2 DATA3 DATA4 DATA5 CONTENT DATA1 DATA2 DATA3 DATA4 DATA5 WRAPPER4 SEQUENZ1 CONTENT DATA1 DATA2 DATA3 DATA4 DATA5 [...]
Jedes einzelne
CONTENT
Element soll nun einen Mantel erhalten und separat in einer Datei abgelegt werden.
Der "Umschlag" soll dabei alle Elemente des Rahmens der Transmission erhalten. Das ist alles auf der
Descendant-Achse bis zum Element
WRAPPER3
, ausserdem noch die Elemente
SEQUENZ1
und
SEQUENZ2
,
sowie das Element
WRAPPER4
mit Kind
SEQUENZ1
. Ohne groß auf die Performanz zu achten, könnte das recht einfach so realisiert werden:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> <xsl:output method="xml" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="/"> <xsl:apply-templates select="/WRAPPER1/WRAPPER2/WRAPPER3/CONTENT" mode="umschlag"/> </xsl:template> <xsl:template match="CONTENT" mode="umschlag"> <xsl:result-document href="{concat(@id,'.xml')}"> <umschlag> <metadaten><!-- einige Metadaten --></env:metadata> <nutzdaten> <xsl:apply-templates select="ancestor::WRAPPER1"> <xsl:with-param name="this-id" select="generate-id()" tunnel="yes"/> </xsl:apply-templates> </nutzdaten> </umschlag> </xsl:result-document> </xsl:template> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="CONTENT"> <xsl:param name="this-element" tunnel="yes"/> <xsl:if test="$this-id = generate-id()"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:if> </xsl:template> </xsl:stylesheet>
Im rekursiven Abstieg wird im Modus "umschlag" jedes
CONTENT
Element selektiert und
in einen Umschlag verpackt. Der eigentlich Inhalt des Umschlags wird generiert,
indem der gesamte XML Baum über die Standard-Kopierregel in das Element
<nutzdaten>
gesetzt wird. Dabei wird aber nur derjenige
CONTENT
Abschnitt evaluiert, der - zu der als Parameter übergebenen - generierten Id passt.
4.1.6.3 Verlinkung auf nächstes Verweisziel
Beispiel Publishing: In einer Publikation sind Grafiken vorhanden, auf die verlinkt werden soll. Das hört sich einfach an, jedoch gibt es auch Fälle, in denen ein solches Verweisziel mehrfach im Buch vorhanden ist.
Man sollte dann z.B. nicht auf dieselbe Grafik in Kapitel 1 verweisen, sondern besser auf die Grafik, die am nächsten liegt. Das kann in Blätterrichtung, aber auch entgegen der Blätterrichtung sein.
Abgesehen davon, dass es natürlich mehr Sinn macht, auf die Vorgänger-Grafik zu verweisen, weil der Leser ja beim Blättern diese schon begutachtet hat und nur zurückblättern muss (anstatt schnell vorzublättern und Inhalte zu überspringen), ist dieses Problem nicht ganz trivial:
1. | Das nächst gelegene Verweisziel findet man über die preceding und following XPath-Achse: |
<xsl:variable name="nearest-preceding" select="preceding::*[@id = current()][1]"/> <xsl:variable name="nearest-following" select="following::*[@id = current()][1]"/>
2. | Welches Verweisziel ist aber nun näher? Um diese Frage zu beantworten, müsste man irgendwie die Distanz zwischen den Knoten messen können. |
3. | Wenn man öfters mit der preceding und following Achse zu tun hat, merkt man schnell, dass die häufige Selektion in diesen beide Achsen auf die Performanz geht. Wie kann man die Performanz hier optimieren? |
Neben der
generate-id()
Funktion werden zwei weitere - sehr clevere - XSLT Konstrukte zur Beantwortung dieser Fragen verwendet:
-
xsl:key um die gesuchten Elemente zum schnellen Auffinden in einen Index aufzunehmen.
-
Den relativ neuen << -Operator, um die Postion des aktuellen Elements im DOM Baum zu bestimmen.
Mit diesen beiden Konstrukten, kann man beispielsweise die Anzahl aller Paras bis zu einer gewissen Position im DOM berechnen. Das geht so:
<xsl:key name="paras" match="p" use="true()"/> [...] <xsl:variable name="current-para-count" select="count(key('paras',true())[. << current()])"/>
Zieht man den Para-Count das erste Vorgänger-Verweisziels davon ab, hat man einen Distanzwert bestimmt.
Jetzt kann man dasselbe für das erste Nachfolger-Verweisziel machen und vergleichen.
Mit diesem Vorgehen lässt sich schon Punk 2.) aus obiger Liste beantworten, denn das nächste Verweisziel, egal ob in Blätterrichtung oder entgegen der Blätterrichtung kann bestimmt werden.
Packt man diese Erkenntnisse in ein XSLT Stylesheet, dann sieht das ungefähr so aus:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0"> <xsl:key name="ids" match="@id" use="."/> <xsl:key name="paras" match="para" use="true()"/> <!-- Vergabe einer neuen, eindeutigen ID an den Verweiszielen, so dass auf das --> <!-- nächstgelegene Verweisziel verwiesen werden kann. --> <xsl:template match="@id[count(key('ids',.)) gt 1]"> <xsl:attribute name="id" select="concat(.,'_',generate-id(parent::*))"/> </xsl:template> <!-- Vergabe einer neuen Ziel-ID an den Links --> <xsl:template match="@target-id[count(key('ids',.)) gt 1]"> <xsl:variable name="nearest-preceding" select="preceding::*[@y.id = current()]"/> <xsl:variable name="nearest-following" select="following::*[@y.id = current()]"/> <xsl:choose> <xsl:when test="$nearest-preceding and $nearest-following"> <xsl:variable name="current-para-count" select="count(key('paras',true())[. << current()])"/> <xsl:variable name="nearest-preceding-para-count" select="count(key('paras',true()) [. << $nearest-preceding])"/> <xsl:variable name="nearest-following-para-count" select="count(key('paras',true()) [. << $nearest-following])"/> <xsl:variable name="distance-to-preceding" select="$current-para-count - $nearest-preceding-para-count"/> <xsl:variable name="distance-to-following" select="$nearest-following-para-count - $current-para-count"/> <xsl:attribute name="target-id" select="if ($distance-to-preceding le $distance-to-following) then concat($nearest-preceding/@y.id, '_',generate-id($nearest-preceding)) else concat($nearest-following/@y.id, '_',generate-id($nearest-following))"/> </xsl:when> [...] </xsl:choose> </xsl:template>