position()
position() gibt die Nummer des aktuell in Bearbeitung befindlichen
Templates zurück. Achtung: Diese Nummerierung unterscheidet nicht
die Art des Tags, nach einem apply-templates select="*" auf
<hallo>
welt und
<eintag/>
<eintag/>
</hallo>
wäre Position also 1 bei hallo und 2 bzw. 3 bei eintag
So funktioniert's richtig:
<xsl:template match="//hallo">
<xsl:apply-templates select="*[not(self::eintag)]"/> <!-- not(self::eintag) bedeutet: zuerst einmal alle Tags bearbeiten, die NICHT eintag-tags sind. -->
<xsl:apply-templates select="eintag"/>
</xsl:template>
<xsl:template match="eintag">
Position: <xsl:value-of select="position()"/><br/>
<xsl:value-of select="."/>
</xsl:template>
last()
last() gibt die Anzahl der Templates zurück, die anzuwenden sind.
Es eignet sich somit hervorragend um in einem xsl:if zusammen
mit einem position() zu überprüfen, ob gerade das letzte
(vorletzte etc.) Tag einer bestimmten Gattung bearbeitet wird.
<xsl:if test="position()=last()">
Letztes Tag dieser Template-Reihe
</xsl:if>
xsl:number
*count *level *format
<xsl:number count="item">
nummeriert die entsprechenden Tags (bei diesem Aufruf:
Nummerierung startet bei 1 und zählt jeweils nur die Tags
der entsprechenen Verschachtelung - Ergebnisse wie 1,2,1,2,3,4,3,5 sind
vorprogrammiert). Das Attribut level="any" anstelle match="item" nummeriert
durchgehend durch alle Ebenen - aber auch das ist nicht das was ich unter
einer Gliederung verstehe. Diese leistet erst level="multiple" count="item".
Das (optionale) Format-Parameter kann noch viel mehr:
format="1.1.1.1" nummeriert nach arabischen Zahlen (Standart)
format="i.1.I.1" nummeriert nach kleinen römischen, dann arabischen, dann
großen römischen und dann wieder arabischen Zahlen (bspw:ii.2.IX.9)
Sollten Sie nicht genug Format-Anweisungen gegeben haben (z.B.: i bei einer
Schachtelungstiefe von 2) werden die nichtangegebenen Ziffern arabisch
(z.B. iii.3.1).
Weitere Beispiele:
|
c.1
|
c.1 für das erste, d.1 für das zweite top-level Element
|
|
3.1
|
1.1 für das erste, 2.1 für das zweite top-level Element!
Hier zählt offensichtlich: Wenn eine Zahl gemeint ist fange bei 1 an.
|
|
Token
|
Sequenz
|
|
01
|
01,02,03...10,11
|
|
andere Unicodes-Nummern
|
entsprechend
|
|
a
|
a,b,c..x,y,z,aa,ab...
|
|
A
|
A,B,C..X,Y,Z,AA,AB...
|
|
i
|
i,ii,iii..ix,x,xi...
|
|
I
|
I,II,III..IX,X,XI...
|
alle nichterkannten Zeichen sind offenbar als Trennzeichen gültig,
z.B. - und *. Interessant genug: nichterkannte Zeichen (wie
schließende Klammern) am Ende
des Format-Strings werden offensichtlich automatisch ans Ende der
Ausgabe angefügt, siehe Beispiel-XML und Beispiel-XSL.
document
string
Mit document('dateiname.xml') kann man in XPath Tags aus anderen
Dateien matchen (für Inhaltsverzeichnisse und Stichwortlisten meist
unerlässlich). Das geht überraschend einfach z.B. mit
<xsl:for-each select="document('xslt6.xml')//hello-world">
<xsl:value-of select="."/>
</xsl:for-each>
Da Dateinamen immer eindeutig sein müssen (sprich: *.xml geht nicht)
ist jedoch meistens Projektdateien-anlegen angesagt. In einer
Datei project.xml legt man dann z.B. in File-Tags mit den Attributen
name alle Dateinamen des Projektes ab, durchforstet sie mit einer
foreach-Schleife und inenrhalb dieser Schleife setzt man die Schleife
für die eigentlichen Tags an:
<xsl:for-each select="document('xslt6.xml')//file">
<xsl:variable name="filename" select="concat(substring-before(@name, '.xml'), '.html')"/>
<xsl:for-each select="document(@name)">
<a href="{$filename}"/>
<xsl:value-of select="//article/@name"/>
<br/>
<xsl:value-of select="//article/@name"/>
<br/> </xsl:element>
</xsl:for-each>
</xsl:for-each>
Obiges Beispiel geht sogar noch einen Schritt weiter: in der Projekt-Datei
werden die Dateinamen natürlich als .XML abgelegt, die concat(substring-before(@name, '.xml'),'.html')
- Anweisung sorgt aber dafür, dass der Dateiname abgeschnitten wird und als .html (!)
gelinkt wird (hoffen wir, dass diese erzeugt wurden!).
Solche document-aufrufe sind übrigens ein fortgesetztes Ärgernis, weil
Sie immer Fehler melden, wenn nicht alle Dateien im Projekt vorhanden und
well-formed sind.
Das Beispiel xslt6.xsl
(mit xslt6.xml) zeigt ausführlich eine Anwendung
(Auflistung von Dateien und aufzählen der enthaltenen Bilder und auflistung der Bilder mit
Link auf die entsprechenden Dateien).
xsl:copy
Der Container für xsl:copy-of.
<xsl:template match="a|abbr|acronym|address|b|big|blockquote|br|cite|dfn|div|em|h1|h2|h3|h4|h5|h6|hr|i|kbd|p|pre|q|quote|samp|span|small|strong|sub|sup|tt|var|button|fieldset|form|input|label|legend|option|optgroup|select|caption|col|colgroup|table|tbody|td|tfoot|th|thead|tr|dl|dd|dt|ol|ul|li|img" xmlns:html="http://www.w3.org/1999/XSL/some">
<xsl:copy>
<xsl:copy-of select="@*"/><!-- alle Attribute und Tags kopieren -->
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
kopiert einen Großteil der XHTML 1.1-Tags aus dem Ursprungsdokument
in die Ausgabe und wird nebenbei auch für dieses Dokument genutzt
(xhtmlxsl.xsl ist in documl.xsl eingebunden).
Außer um Tags in die Ausgabe "durchzuschleifen" kann dieses Tag natürlich auch
verwendet werden um einen Abschnitt aus derselben oder einer anderen Quelldatei
an die entsprechende Stelle zu kopieren.
xsl:copy-of
select
Kopiert Tags. Im Beispiel werden z.B. einige HTML-Tags bei
Vorkommen ungeändert und mit allen Attributen in die Ausgabe kopiert.
xsl:import
xsl:import importiert ein Style-Sheet. Wichtig ist nur zu
beachten, dass es ein top-level-Element ist, also als Kind zu
xsl:stylesheet "sehr weit oben" in der Datei stehen muss.
xsl:import importiert mit einer niedrigeren Import-Präzedenz
als xsl:include, d.h. dass in der lokalen Datei matchende
templates den importierten auf jeden Fall vorgezogen werden
(anders bei xsl:include).
<xsl:import href="common.xsl"/>
importiert die Style-Sheet-Datei common.xsl.
xsl:include
xsl:import importiert ein Style-Sheet als wäre es an dieser Stelle
im Quelltext definiert. Es ist wie xsl:import ein top-level-Element.
<xsl:include href="common.xsl"/>
fügt die Style-Sheet-Datei common.xsl an diese Stelle ein.
In XSLT-Steuerdateien gilt: first comes first serves,
also wird bei zutreffende Templates, sofern doppelt definiert, nur
das erste treffende Template angewendet. Sollte also in der
Include-Datei ein zutreffendes template definiert sein, wird dieses
nur dann nicht angewendet, wenn in der XSLT-Datei vor der xsl:include-Zeile
bereits ein treffendes Template angewand wurde.
substring-before()
value, substring
Liefert den Stringteil von value zurück, der VOR Substring steht.
substring-after()
value, substring
Liefert den Stringteil von value zurück, der NACH Substring steht.
concat()
str1, str2
Liefert einen aus str1 und str2 zusammengesetzten String zurück.
xsl:call-template
name
Ruft das Template name auf (man beachte die Ähnlichkeit zu einem Funktionsaufruf).
<xsl:template name="mytemplate">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="mytemplate"/>
</xsl:template>
Eine dateiübergreifende Referenz sortieren
Eine Referenz zu erstellen, ist in XSLT einfach mit einem
for-each-Tag möglich;
aus verschiedenen Dateien liest man mit document.
Normalerweise erstellt man eine XML-Datei in der man alle XML-Dateien erwähnt, die
am aktuellen Projekt beteiligt sind:
<project>
<file id="datei1" name="datei1.xml"/>
<file id="datei2" name="datei2.xml"/>
</project>
Diese kann man dann z.B. mit xsl:for-each select="document('project.xml')//file" ansprechen.
In diesem Beispiel habe ich jeder Datei noch eine ID gegeben, die in der
Datei selbst dann auch irgendwo auftauchen sollte - so spreche ich z.b.
in diesem Projekt in project.xml die following-sibling bzw. preceding-siblings
des files an, das die gleiche ID hat wie die aktuelle Datei um
Links auf die nächste bzw. vorhergehende Datei zu zeigen (in XSLT ist es normalerweise
unmöglich, den Dateinamen der aktuelle in bearbeitung-befindlichen-Datei herauszufinden).
Das Problem ist nun das, dass ich in der Schleife, die die Referenzpunkte sortiert,
nur die einer XML-Datei ansprechen kann (for-each select="document($documentname)//ref");
<xsl:for-each select="document(document('project.xml')//file/@name)//ref"> geht nämlich
NICHT.
Die Lösung ist die, dass man alle //ref's aus allen Dokumenten erst einmal in eine Variable kopieren muss
(diese wird damit selbst zum Baum), dann die Variable sortiert und ausgibt. Das gesamte ref-Tag kopieren
ist in dem Falle wichtig, weil man schlecht sortieren kann, wenn man nur mit Strings
arbeitet und: das ref-Tag enthält oft auch ein Verweisziel, wie einen Dateinamen, der
dann an eine andere Stelle kopiert werden würde.
<xsl:variable name="nodetree">
<xsl:for-each select="document('project.xml')//file">
<file xmlns="" name="{@name}">
<-- hier wird in der neuen Baumvariable ein Element angelegt das den Dateinamen enthält, in welchem die refs gerade stehen-->
<xsl:copy-of select="document(@name)//ref"/>
<-- hier werden die Ref-tags als Unterelement zum Dateinamen in den Baum kopiert -->
</file>
</xsl:for-each>
</xsl:variable>
<h2>Nach Stichwort</h2> <-- Nach Stichwort sortieren -->
<xsl:for-each select="$nodetree//ref"> <-- alle Refs wieder aus dem Baum raus-->
<xsl:sort select="@name"/> <-- nach Attribut name sortieren-->
<a href="{../@name}"><xsl:value-of select="@name"/></a><br/>
<-- Und zusammen mit Link auf das Name-Attribut des übergeordneten Elements (File) ausgeben-->
</xsl:for-each>
<h2>Nach Datei</h2> <-- Nach Datei sortieren -->
<xsl:for-each select="$nodetree//file">
<xsl:sort select="@name"/>
<xsl:for-each select="//ref">
<xsl:sort select="@name"/> <-- Jeden Dateiinhalt nach Stichwort sortieren -->
<a href="{../@name}#{../@a}"><xsl:value-of select="@name"/></a><br/><-- ... und ausgeben -->
</xsl:for-each>
</xsl:for-each>
Die in diesem Projekt verwendete Transformation ist nochmal ein bisschen schwerer weil
die Links genauer sein sollten und auf jeden Absatz zielen - das heißt letztendlich,
dass ich ich die Sections (s-tags) in den Sortierbaum aufnehmen musste...
Zum debuggen empfehle ich übrigens mit copy-of select="$baumvariable" die Variable
erstmal in den HTML-Quelltext zu kopieren um dann dort zu sehen ob sie funktioniert.
What's missing
Es trifft nicht ganz zu, diesen Artikel "für Profis" genannt zu haben;
Das XSLT ein bisschen mehr ist, als hier erwähnt wurde,
"wäre ungefähr wie zu sagen, die Haut eines Ertrinkenden sei feucht.
Es ist richtig aber es trifft nicht den Kern der Sache" (Scott Adams).
XSLT ist ein weitaus mächtigeres Werkzeug als ich bei meinen ersten
Style-Sheets gedacht habe - hoffentlich können Sie mit diesen Dateien
schonmal anfangen, Ihre ersten Style-Sheets zu schreiben und vielleicht
springt der Funke über...