copyright (c)2003-2005   useGroup
XSLT

66151@usegroup.de

Einführung in XML/XSLT

XSLT für Profis

nach untenposition()

nach untenlast()

nach untenxsl:number

nach untendocument

nach untenxsl:copy

nach untenxsl:copy-of

nach untenxsl:import

nach untenxsl:include

nach untensubstring-before()

nach untensubstring-after()

nach untenconcat()

nach untenxsl:call-template

nach untenEine dateiübergreifende Referenz sortieren

nach untenWhat's missing

position()

nach obenposition() 

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()

nach obenlast() 

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

nach obenxsl:number 

<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

nach obendocument 

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

nach obenxsl: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

nach obenxsl:copy-of 

Kopiert Tags. Im Beispiel werden z.B. einige HTML-Tags bei Vorkommen ungeändert und mit allen Attributen in die Ausgabe kopiert.



xsl:import

nach obenxsl: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

nach obenxsl: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

nach obensubstring-before() 

Liefert den Stringteil von value zurück, der VOR Substring steht.



substring-after()
value, substring

nach obensubstring-after() 

Liefert den Stringteil von value zurück, der NACH Substring steht.



concat()
str1, str2

nach obenconcat() 

Liefert einen aus str1 und str2 zusammengesetzten String zurück.



xsl:call-template
name

nach obenxsl:call-template 

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

nach obenEine 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

nach obenWhat'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...



copyright (c)2003-2005   useGroup