XSLT - eXtensible Stylesheet Language Transformation
XSLT ist eine Transformationssprache für XML-Dokumente. Ein XSLT-Prozessor verarbeitet ein XML-Dokument gemäß der Angaben in einem XSLT-Dokument und erzeugt ein Ergebnisdokument (oder mehrere) in einem Format (z. B. Plain Text, XML, HTML), welches vom XSLT-Dokument bestimmt wird.
Institut für Dokumentologie und Editorik: XML - Kurzreferenz für Einsteiger (Hervorhebungen nicht im Original)
XSLT liegt inzwischen in der dritten Version (3.0) vor.
XSLT ist eine deklarative Programmiersprache.
Many [declarative programming] languages […] [describe] what the program must accomplish in terms of the problem domain, rather than describe how to accomplish it as a sequence of the programming language primitives (the how being left up to the language's implementation [in a lower level language]). This is in contrast with imperative programming, which implements algorithms in explicit steps.
Wikipedia: Declarative_programming ID: 846955022
Weitere deklarative Sprachen sind bspw. SQL, SPARQL und Cypher. Die bekannteren deklarativen Sprachen dienen der Daten-Abfrage und -Akquise.
Hinweis: In den Libraries vieler Programmiersprachen wurde XSLT nur bis einschließlich Version 2.0 implementiert. Eine umfassende Implementierung von XSLT 3.0 existiert nur als Saxon-C für Java, C/C++, PHP und neuerdings auch für Python 3 (hierzu mehr hier und hier).
Hinweis: Extraktionen sind im Grunde Spezialfälle von Transformationen.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0"></xsl:stylesheet>
Alternativ kann auch das Synonym
<xsl:transform>
verwendet werden. Die Gründe sind
historischer Art.
Wichtig: Ein XSL-Dokument ist stets ein wohlgeformtes XML-Dokument!
Wichtig: Die Angabe des@xmlns:xsl
-Namespaces ist obligatorisch!
<xsl:stylesheet
xpath-default-namespace="http://www.tei-c.org/ns/1.0" 1
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 2
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all" 3
version="3.0"> 4
<xsl:output indent="yes" method="xml"/> 5
<xsl:mode on-no-match="shallow-copy"/> 6
</xsl:stylesheet>
1 @xpath-default-namespace
definiert den
TEI-Namespace als standardmäßigen Namespace.
2 Alle weiteren relevanten Namespaces werden über das Präfix
definiert, e.g. @xmlns:xsl
3 @exclude-result-prefixes
schließt mittels
#all
alle Namespaces aus der Ergebnisdatei aus.
4 @version
wird auf 3.0
gesetzt, um
die neuesten Features zu nutzen.
5 output
…
… sorgt per @indent
für
automatisches Code-Formatieren der Ergebnisdatei.
… sorgt per @method
dafür, dass
xml
als Ausgabe produziert wird.
6 mode
…
… sorgt mit @on-no-match
und dem Wert
shallow-copy
dafür, dass Knoten, für die im
Push-Workflow kein passendes Template existiert, unverändert in den
Ergebnisbaum kopiert werden.
Tipp: Das Beispiel oben ist ein guter Ausgangspunkt zur Transformation und Extraktion von TEI-XML in allgemeines XML.
xsl:template
-Elemente sind Kind-Elemente des
xsl:stylesheet
-Elements<xsl:template match="/root" >
/* Verarbeitet spezifische Knoten via XPath-navigation, e.g. */
<xsl:value-of select="head/title" />.
/* Der aktuelle Kontext-Knoten ist "/root". */
</xsl:template>
xsl:template
-Element vorhanden sein, das per
XPath-Ausdruck
den Einstiegsknoten des
XSL-Prozesses im Quelldokument
matcht
.xsl:template
-Elementes können durch spezifische
Kind-Elemente Knoten- und XML-Fragmente kopiert oder
weitere Template-Aufrufe gesteuert werden.xsl:template
addressieren ihre
Zielbereiche innerhalb der Quelldatei ebenfalls über
XPath-Ausdrücke
; hierbei wird aber der vom
Eltern-Element adressierte Knoten als
Kontextknoten angesehen.xsl:value-of
<xsl:template match="/TEI">
<document>
<meta>
<title>
<xsl:value-of 1 select="teiHeader/fileDesc/titleStmt/title"2/>
</title>
</meta>
<persons>
<p>Add the xsl for persons hear.</p>
</persons>
</document>
</xsl:template>
xsl:value-of
) sowie Knoten- und XML-Fragmenten
(xsl:copy-of
) aus dem Quelldokument über das XSL-Template
in das Ziel-Dokument.@select
wird dabei per
XPath das Ziel in der Quelldatei angesteuert. An dieser Stelle können auch
weitere XPath-Funktionen
angebracht werden.Übung: Bitte öffnen Sie die DateienUebungen/Sturm_persList.xml
undUebungen/base_xslt.xsl
, kopieren Sie obigen Code an die korrekte Stelle in der Datei und ergänzen Sie den Header-Bereich indocument/meta
durch weitere Information aus dem TEI-Header der Quelldatei.
xsl:for-each
(Schleifen)<xsl:template match="/TEI">
<document>
<meta>
<p>Add the xsl for meta information aggregation here.</p>
</meta>
<persons>
<xsl:for-each 1 select="text/body//listPerson/person"2>
<name><xsl:value-of select="persName"/>3</name>
</xsl:for-each>
</persons>
</document>
</xsl:template>
xsl:for-each
kann per XPath in
2 @select
eine Liste an Knoten gesammelt werden, deren
einzelne Elemente von den 3 Kind-Elementen des xsl:for-each
iteriert und weiterverarbeitet
werden.@select
können auch
weitere XPath-Funktionen
angebracht werden.xsl:for-each
können
verschachtelt werden.Übung: Bitte öffnen Sie die DateienUebungen/Sturm_persList.xml
undRessourcen/XSLT/Sturm_persList.xsl
und ergänzen Sie denxsl:for-each
-Bereich indocument/persons
durch weitere Information aus der Quelldatei.
<xsl:template match="/TEI">
<document> <meta> <p>Add the xsl for meta information aggregation here.</p> </meta>
<persons>
<xsl:for-each 1 select="text/body//listPerson/person"2>
<xsl:apply-templates 4 select="."3/>
</xsl:for-each>
</persons>
</document>
</xsl:template>
5<xsl:template 6 match="person" 7>
8<record type="person"><xsl:apply-templates 9/></record>
</xsl:template>
5<xsl:template match="persName">
<name><xsl:apply-templates/></name>
</xsl:template>
5<xsl:template match="linkGrp" />
xsl:for-each
per XPath in
2 @select
angesteuerte Knoten, wird nun über das
3 @select="."
des Kind-Elementes
4 xsl:apply-templates
in die
“Template-Rekursion” übergeben.xsl:template
) durchgeht und prüft ob der
Xpath-Ausdruck in 6 @match
auf den Kontextknoten, im Beispiel
7 person
genannt, passt.@match
erzielt wird, wird der
8 Inhalt des Templates ausgeführt und der aktualisierte Kontext gegebenenfalls
wieder per 9 xsl:apply-templates
in die Rekursion
zurückgespielt.Übung: Bitte erläutern Sie, 1) warum die Tagsperson
nicht in der Ausgabedatei auftauchen. 2) warumptr
-Tags nicht in der Ausgabedatei auftauchen.
Bitte schreiben Sie weitere Templates, um weitere Informationen in der Ausgabedatei auszugeben.
XSLT 1.0 & 2.0 verfügen nicht über Konfigurationsmöglichkeiten, die es gestatten, festzulegen, wie mit Knoten umgegangen werden soll, für die kein “matchendes” Template existiert. In diesen Fällen greifen die sogenannten “hidden templates”. Diese führen dazu, dass es bei fehlendem Template-Match immer zu einem Minimal-Output an Text(-knoten) kommt. Alle sonstigen Knoten gehen bei diesem “Notfall-Match” verloren.
Will man in XSLT 1.0 & 2.0 sämtliche Knoten, für die kein Template definiert wurde, erhalten, muss ein sogenanntes “Identity Transform-Template” eingebunden werden, dessen Regel derart allgemein ist, dass alle Knoten gematcht werden, für die kein spezialisierteres Template existiert.
<xsl:template match="@*|*|processing-instruction()|comment()">
<xsl:copy>
<xsl:apply-templates select="*|@*|text()|processing-instruction()|comment()"/>
</xsl:copy>
</xsl:template>
Oder kürzer (aber weniger explizit):
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
In XSLT 3.0 kann der Umgang mit Knoten, für die es keine matchenden Templates gibt mit xsl:mode
und @on-no-match
, konfiguriert werden. Siehe zu den verfügbaren Werten hier.
<xsl:stylesheet
/* Snip */
version="3.0">
<xsl:output indent="yes" method="xml"/>
<xsl:mode on-no-match="shallow-copy"/>
</xsl:stylesheet>
In XSLT können die bekannten XPath-Funktionen genutzt werden.
count()
, substring()
,
string-length()
contains()
, matches()
, ...not()
translate()
, replace()
, ...string()
, number()
, ...In XSLT können innerhalb eines Templates Variablen gesetzt und abgerufen werden. String-Interpolation erfolgt über 1 {$var_name}
.
<xsl:template match="commentary">
<xsl:variable name="ID" select="ancestor::resource/@id"/>
<div type="commentary" ancestor="{$ID}"1>
<xsl:apply-templates select="." />
</div>
</xsl:template>
In XSLT können 1 Variablen und 2 Parameter gesetzt und innerhalb eines Templates abgerufen werden. Parameter können zudem im xsl:apply-templates
an nachfolgende Templates übergeben werden. String-Interpolation erfolgt über {$var_name}
.
<xsl:template match="commentary">
<xsl:variable 1 name="ID" select="ancestor::resource/@id"/>
<div type="commentary">
<xsl:apply-templates select=".">
<xsl:with-param 3 name="ID" select="$ID"/>
</xsl:apply-templates>
</div>
</xsl:template>
<xsl:template match="sup">
<xsl:param 2 name="ID" />
<hi rend="superscript">
<ref xml:id="{$ID}" target="#apparatus_{$ID}"><xsl:apply-templates /></ref>
</hi>
</xsl:template>
Hinweis: Wenn einexsl:variable
per 3xsl:with-param
in der Rekursion weitergereicht wird, muss sie in nachfolgenden Templates mitxsl:param
2 “gefangen” werden, um nutzbar zu sein. Soll sie in der Rekursion weiter verfügbar sein, muss die Variable zudem erneut imxsl:apply-templates
-Aufruf perxsl:with-param
weitergegeben werden.
Mit der 1 document()
-Funktion können über eine URI
(Pfad im Dateisystem, URL zu einer Web-Ressource) in den laufenden XSL-Prozess einbezogen werden.
Die per document()
eingebundene Datei kann durch weitere Templates und Funktionen weiterverarbeitet werden.
<xsl:template match="person">
<person>
<name><xsl:value-of select="persName" /></name>
<pre>
<code>
<xsl:copy-of 1 select="document(concat('https://lobid.org/gnd/',@gnd,'.rdf'))/node()" />
</code>
</pre>
</person>
</xsl:template>
Wichtig: Beim Einbinden und Verarbeiten externer Ressourcen muss darauf geachtet werden, gegebenenfalls neu auftretende Namespaces adäquat zu verarbeiten.
In XSLT können einfache und komplexe Conditionals gesetzt werden:
xsl:if
; nur if.xsl:choose
; äquivalent zu 3if/4elif/5else<xsl:template match="person">
<p class="person">
1<xsl:if test="@title">
<span class="title"><xsl:value-of select="@title" /></span>
</xsl:if>
<xsl:text> </xsl:text>
<xsl:apply-templates select="." />
2<xsl:choose>
3<xsl:when test="@gender = 'male'">
<xsl:text> (♂)</xsl:text>
</xsl:when>
4<xsl:when test="@gender = 'female'">
<xsl:text> (♀)</xsl:text>
</xsl:when>
5<xsl:otherwise>
<xsl:text> (?)</xsl:text>
</xsl:otherwise>
</xsl:choose>
</p>
</xsl:template>
In XSLT können innerhalb eines Templates Funktionen erstellt und aufgerufen werden.
as="xs:string"
gibt den Rückgabewert einer Funktion oder den Datentyp einer Parameterdeklaration.xsl:sequence
ist äquivalent zum Keyword return
in anderen Programmiersprachen und gibt das Ergebnis der Funktion zurück.Namespace
deklariert werden.<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
…
3 xmlns:functx="http://www.functx.com" …/>
…
<xsl:function 3 name="functx:substring-after-match" 1 as="xs:string?">
<xsl:param name="arg" 1 as="xs:string?"/>
<xsl:param name="regex" as="xs:string"/>
2 <xsl:sequence select="replace($arg,concat('^.*?',$regex),'')"/>
</xsl:function>
Siehe auch Artikel auf Data2Type.
Man erstellt …
<xsl:element name="element_name">
<xsl:attribute name="attribute_name">
<xsl:template match="person">
1 <xsl:element name="name">
2<xsl:attribute name="norm_data">
3<xsl:value-of select="@gnd"/>
</xsl:attribute>
4<xsl:value-of select="persName"/>
</xsl:element>
</xsl:template>
# Headline
I’am a paragraph.
## Second Order Headline
I am a paragraph as well.
unparsed-text()
geladen werden (am besten in eine 2 Variable).<xsl:template match="/">
2 <xsl:variable name="rawtext_input" select="unparsed-text('rawtext.md')" 1/>
<root>
<xsl:for-each select="tokenize($rawtext_input, '
')" 3>
<xsl:variable name="line" select="normalize-space(.)"/>
4 <xsl:choose>
<xsl:when test="starts-with($line, '#')">
<xsl:element name="h{string-length(substring-before($line, ' '))}">
<xsl:value-of select="normalize-space(substring-after($line, ' '))"/>
</xsl:element>
</xsl:when>
<xsl:when test="$line != ''">
<xsl:element name="p">
<xsl:value-of select="$line"/>
</xsl:element>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:for-each>
</root>
</xsl:template>
Output:
<root>
<h1>Headline</h1>
<p>I’am a paragraph.</p>
<h2>Second Order Headline</h2>
<p>I am a paragraph as well.</p>
</root>