xsl:number

(Auszug aus "XSLT 2.0 & XPath 2.0" von Frank Bongers, Kapitel 2.)

XSLT wartet hier mit der Instruktion xsl:number auf. Diese vollführt zwei Funktionen: Sie kann verwendet werden, um dem Current Item einer verarbei­teten Sequenz eine fortlaufende Bezifferung hinzuzufügen, oder sie kann eine Zahl (die sich aus dem optionalen value-Attribut ergibt) für die Ausgabe in die Ergebnissequenz mittels des Musterstrings ihres format-Attributs formatieren.

Beide Aktionen können sowohl gemeinsam als auch unabhängig voneinander ausgeführt werden. Die Zahl, die durch das value-Attribut eingefügt werden soll, kann durch einen XPath-Ausdruck erzeugt werden. Dieser Ausdruck wird ausgewertet, das Ergebnis (wenn erforderlich) in eine Bezifferung umgewan­delt und diese als Textknoten ausgegeben. Einige dieser Attribute werden gleich vorgestellt. Für eine vollständige Erörterung von xsl:number möchte ich Sie jedoch ausdrücklich auf den Referenzteil verweisen.

Grundsätzlich ist xsl:number immer ein leeres Element. Die einfachste Anwendung der Instruktion besteht darin, sie dorthin zu schreiben, wo man eine Nummerierung wünscht: Was dann gezählt wird, hängt vom jeweiligen Current Node ab.

Folgendermaßen kann die Buchliste des Buchkatalogs ganz durchnummeriert werden:

<xsl:template match="buch">
  <p><xsl:number/><xsl:apply-templates/></p>
</xsl:template>

Code-Beispiel: kap02/2.03.2/buchhandel-number1.xsl (Auszug).

Hier werden dem Buchtitel einfach blanke Ziffern vorangestellt, die jeweils der Position des ausgegebenen Buch-Elements in der Dokumentreihenfolge entsprechen, etwa so:

3 Unter Korallen und Haien von Hass, Hans;
Verlag: Ozean Verlag; Preis: 22.95 Euro
(ISBN liegt nicht vor.)

Gezählt werden Preceding-Siblings des aktuellen Knotens, und zwar nur sol­che, die diesem gleichen – in diesem Fall <buch>-Elemente. Hier liegen zwar keine andersartigen Geschwisterknoten vor, wäre dem aber so, würden sie ignoriert.

Das format-Attribut

Die Ausgabe nackter lateinischer Zahlen ist Default-Verhalten. Soll die Numme­rierung anders erfolgen, so muss das format-Attribut bemüht werden:

<xsl:number format="a "/>

Hiermit wird folgende, für eine solche Liste wenig sinnvolle Ausgabeform mit einer Bezifferung durch Kleinbuchstaben erzielt. Die Zahl der Buchstaben ist begrenzt, was am Ende des Alphabets zu lustigen Ergebnissen führt:

a Where Wizards Stay Up Late von Hafner, Katie; Lyon, Matthew;
Verlag: Touchstone; Preis: 14.00 Dollar
(ISBN: 3-899-77609-7)

...

z Der Steppenwolf von Hesse, Hermann;
Verlag: Suhrkamp; Preis: 7.50 Euro
(ISBN: 3-518-36675-0)

aa Siddhartha von Hesse, Hermann;
Verlag: Suhrkamp; Preis: 11.80 Euro
(ISBN: 3-518-36682-3)

Vertretbarer ist eine römische Bezifferung mit Kleinbuchstaben, die XSLT auch beherrscht. Neben den Bezifferungszeichen können noch weitere Zeichen Teil der Nummerierung sein. Vergleichbar mit Literal Result Elements werden sie unverändert weitergegeben. Hier ist es ein einfacher Punkt:

<xsl:number format="i. "/>

Der Punkt taucht hinter der fortlaufenden Nummerierung wieder auf:

...
ii. Yet Another Hitchhiker's Guide to the Galaxy von Adams, Dou­glas;
Verlag: Spacetrotter Publishing; Preis: 17.00 Dollar
(ISBN: 3-899-77610-5)

iii. Unter Korallen und Haien von Hass, Hans;
Verlag: Ozean Verlag; Preis: 22.95 Euro
(ISBN liegt nicht vor.)

iv. Wunder der Tiefsee von Braun, Bert;
Verlag: Ozean Verlag; Preis: 14.00 Euro
(ISBN: 3-899-77608-5)

Auch das in den vorher verwendeten Werten des format-Attributs stillschwei­gend eingeschmuggelte Leerzeichen zählt zu den Zeichenliteralen. Ohne die­ses würden die Ziffern hier unter Umständen direkt neben dem Buchtitel ste­hen. Wichtig zu wissen, wenn zusätzlich xsl:strip-space eingesetzt wird.

Der format-String stellt also einen Muster-String für den Aufbau der Beziffe­rung dar. Hier ergeben sich unendliche Möglichkeiten, die an dieser Stelle nicht erschöpfend dargestellt werden können. Abschließend vielleicht noch dies hier:

<xsl:number format="001.) "/>

Dies erzeugt eine lateinische Bezifferung mit zwei akkurat umgesetzten füh­renden Nullen, gefolgt von Punkt und runder Klammer:

007.) Ganz oben von Hansen, Carl;
Verlag: Berg Verlag; Preis: 20.95 Euro
(ISBN liegt nicht vor.)
...
023.) Another Hitchhiker's Guide to the Galaxy von Adams, Douglas;
Verlag: Spacetrotter Publi­shing; Preis: 15.00 Dollar
(ISBN liegt nicht vor.)

Es gibt weitere und weniger wichtige Attribute von xsl:number für spezielle Fälle, auf die hier nicht näher eingegangen werden muss: lang, letter-value, grouping-separator, grouping-size. Interessant sind hingegen das count- und das level-Attribut. In Zusammenhang mit Sortierungen wird auch das value-Attribut behandelt.

Das count-Attribut

Das count-Attribut enthält ein Pattern, das bestimmt, welche Knoten im jewei­ligen Dokument-Level gezählt werden sollen. Wenn kein count-Attribut festge­legt ist, so wird jeder Knoten desselben Knotentyps wie der aktuelle Knoten der Template-Regel gezählt (im vorhergehenden Beispiel das Element <buch>).

Das count-Pattern kann Predicates enthalten, um das Zählverhalten von xsl:number gegenüber gleichartigen Geschwistern des Current Node einzu­schränken. Wollen Sie nur solche Listenelemente zählen, für die z. B. ein Attri­but einen bestimmten Wert besitzt, so können Sie dies folgendermaßen errei­chen.

Zunächst die Liste:

...
  <liste>
    <listenelement zaehlen="ja">Listenelement</listenelement>
    <listenelement zaehlen="nein">Nee, gilt nicht</listenelement>
    <listenelement zaehlen="ja">Listenelement</listenelement>
    ...
  </liste>
...

mit dieser Template-Regel:

<xsl:template match="liste/listenelement">
  <div>
    <xsl:number count="listenelement[@zaehlen='ja']"/>
    <xsl:apply-templates/>
  </div>
</xsl:template>

ergibt im Ergebnisdokument:

...
<div>1 Listenelement</div>
<div>Nee, gilt nicht</div>
<div>2 Listenelement</div>

Vorsicht Falle: Hier sollte erwähnt werden, dass nur der Ziffernteil des For­matstrings unterdrückt wird, nicht jedoch die Ausgabe der Zeichenliterale. Mit

<xsl:number count="listenelement[@zaehlen='ja']" format="a) "/>

sähe die Ausgabe etwa so aus:

...
<div>a) Listenelement</div>
<div>) Nee, gilt nicht</div>
<div>b) Listenelement</div>

Eine, zunächst verblüffend anmutende Fähigkeit des count-Attributs ist es, alternativ zum aktuellen Knoten auch auf dessen Ancestor-Achse zu findende Elemente zählen zu können.

Haben wir z. B. ein Buch, das in Kapitel unterteilt ist, und wollen die Titel der Kapitel nummerieren, so muss immer die Anzahl der vorangegangenen <kapi­tel>-Container, einschließlich des unmittelbar den aktuellen <titel> umge­benden gezählt werden. Dies erreichen Sie folgendermaßen:

Für diese Dokumentstruktur

<buch>
  <kapitel><titel>Erstes Kapitel</titel> ...</kapitel>
  <kapitel><titel>Zweites Kapitel</titel> ...</kapitel>
  <kapitel><titel>Drittes Kapitel</titel> ...</kapitel>
  ...
</buch>

mit diesem Template:

<xsl:template match="kapitel/titel">
  <h1>
    <xsl:number count="kapitel"/>
    <xsl:apply-templates/>
  </h1>
</xsl:template>

ergibt sich als Resultat:

...
  <h1>1 Erstes Kapitel</h1>
  ... bla ...
  <h1>2 Zweites Kapitel</h1>
  ... bla ...

Man kann den Sachverhalt damit beschreiben, dass das count-Attribut gewis­sermaßen gleichzeitig auf der Preceding-Sibling- und auf der Ancestor-Or-Self-Achse Ausschau hält. Zunächst wird das count-Pattern geprüft, ob dieses auf den Current Node zutrifft (und zählt gegebenenfalls dessen vorangegangene Siblings), andernfalls geht es vom aktuellen Knoten in der Hierarchie nach oben (Ances­tor-Achse) zu den übergeordneten Elementen, bis es eines findet, auf das dieses Pattern zutrifft. Jetzt wird gezählt, wie viele Preceding-Siblings dieser Elemente aufgetreten sind – fertig.

xsl:number zählt kapitel-Elemente auf der Preceding-Sibling-Achse

Abbildung: xsl:number zählt <kapitel> auf der Preceding-Sibling-Achse.

Das level-Attribut

Mit dem optionalen level-Attribut wird die Nummerierung in Hinblick auf die Position des aktuellen Elements im Elementbaum des Quelldokuments defi­niert. Mit level="multiple" können Sie beispielsweise hierarchische Num­merierungen wie 2.1.5 oder A. 1. a) erzielen. Der Default-Wert ist "single".

Bei level="single" oder nicht gesetztem level-Attribut, stoppt der Prozes­sor beim ersten Ancestor, auf den das count-Pattern passt, und zählt dessen Preceding-Siblings (wie oben). Das funktioniert nur dann gut, wenn sich die Zielknoten zuverlässig auf einer gemeinsamen Hierarchie-Ebene befinden (wie sie in der Abbildung durch das graue Rechteck markiert ist).

Ist dies nicht der Fall, wie es etwa bei Bildern in einem Dokument sein kann, so muss das ganze Dokument in umgekehrter Dokumentreihenfolge ab dem aktu­ellen Knoten durchsucht werden. Jedoch ohne, dass der Prozessor in der Ebene hängenbleibt, in dem der erste Treffer auftritt.

Um genau dies zu verhindern, setzt man das level-Attribut auf "any":

<xsl:template match="bild">
  <!-- Bild darstellen, wie auch immer -->
  <xsl:text>Abb. </xsl:text>
  <xsl:number level="any"/>
  <xsl:text>:</xsl:text>
  <!-- Bildunterschrift erzeugen -->
</xsl:template>

Da das <bild>-Element ohnehin aktueller Knoten ist, muss kein count-Attribut gesetzt werden. Durch level="any" werden alle Bilder gezählt, die vor dem aktuellen Bild irgendwo im Dokument aufgetreten sind.

Das Ergebnis kann z. B. so aussehen:

Abb. 57: die gemeine Hauskatze

Setzen wir das level-Attribut auf den Wert "multiple", so ist eine komple­xere Art der Nummerierung möglich. In Zusammenhang mit count- und for­mat-Attribut können beliebige hierarchische Bezifferungen erzeugt werden.

Besteht ein Dokument aus wiederholten Strukturen der Art »Abschnitt enthält Kapitel« so können wir gleichzeitig die Abschnitte nummerieren, die Kapitel zählen, aus denen jeder Abschnitt besteht, und den verschiedenen Ebenen der Hierarchie verschiedene Zählsymbole zuweisen:

<buchdokument>
  <!-- der erste Abschnitt -->
  <abschnitt>
    <kapitel>
      <ueberschrift>Von diesem...</ueberschrift>
      <absatz>...</absatz>
    </kapitel>
    <kapitel>
      <ueberschrift>Von jenem...</ueberschrift>
      <absatz>...</absatz>
    </kapitel>
    ...
  </abschnitt >
  <!-- der zweite Abschnitt -->
  <abschnitt>
    <kapitel>
      <ueberschrift>Von anderen Dingen</ueberschrift>
      <absatz>...</absatz>
    </kapitel>
    <kapitel>
      <ueberschrift>Von ganz anderen Dingen</ueberschrift>
      <absatz>...</absatz>
    </kapitel>
    ...
  </abschnitt>
  ...
</buchdokument>

Das Ganze soll in Zusammenhang mit der Ausgabe einer Kapitelüberschrift geschehen, wird also in die dazugehörige Template-Regel match="ueber­schrift" verlagert:

<xsl:template match="ueberschrift">
  <h3><xsl:number level="multiple" count="abschnitt|kapitel" format="I a) "/><xsl:value-of select="."/></h3>
</xsl:template>

Der aktuelle Knoten bei der Template-Verarbeitung ist das jeweilige <ueber­schrift>-Element eines Kapitels, auf dessen Ancestor-Achse zum <abschnitt>-Vorfahren zurückgegangen wird, und dieser Knoten sowie des­sen vorhergehenden Geschwisterknoten (preceding siblings) gezählt werden. Auf diesem Weg wird Überschriften aus dem zweiten Abschnitt die Bezifferung II vorangestellt.

Gleichzeitig werden analog die vorhergehenden Geschwisterknoten des über­geordneten <kapitel>-Elements gezählt, da auf dieses ebenfalls das Zählpat­tern der xsl:number-Instruktion zutrifft. Das count-Attribut enthält mit dem Operator »|« ein verbindendes ODER, also die Anweisung »zähle sowohl <abschnitt> als auch <kapitel>«. Die Kapitelbezifferung erfolgt in Klein­buchstaben a, b, c , wie das format-Attribut vorschreibt, wobei noch eine runde Klammer angehängt wird.

Im Ergebnisdokument sieht dies in etwa folgendermaßen aus:

...
  <h3>I a) Von diesem...</h3>
  <!-- Inhalt des Kapitels -->
  <h3>I b) Von jenem...</h3>
  ...
  <h3>II a) Von anderen Dingen</h3>
  ...
  <h3>II b) Von ganz anderen Dingen</h3>
  ...

Hierbei ist zu beachten, dass die Bezifferung von links nach rechts der Doku­menthierarchie folgt. Das count-Attribut nennt lediglich die Elemente, die gezählt werden. Es ordnet also nicht etwa zu, welches Element im format-String durch welchen Zähler repräsentiert wird. Die Reihenfolge der Bezeich­ner im count-Attribut ist daher irrelevant:

count="abschnitt|kapitel" entspricht count="kapitel|abschnitt"

Das hierarchisch an oberster Stufe stehende Element wird also stets gemäß des ersten Format-Token von format nummeriert und so fort.

Das from-Attribut

<xsl:number from="XPath-Pattern"/>

Das from-Attribut bestimmt den Punkt (Node) im Dokument, von dem aus (in Child-Richtung) der Zählvorgang beginnt. Dies kann erforderlich sein, wenn Ele­mente nur innerhalb von bestimmten Dokumentabschnitten gezählt werden sollen. Wie gerade gezeigt wurde, geht xsl:number normalerweise auf der Ance­stor-Achse nach oben, bis es einen zu seinem Pattern passenden Knoten findet.

Ist level auf "any" gesetzt, so wird dabei das gesamte Dokument durchsucht, und ausnahmslos alle entsprechenden Knoten gefunden. Das ist manchmal nicht erwünscht. Es sollen vielleicht nur die Knoten in einem bestimmten Bereich zählen. Mit Hilfe des from-Attributs kann der Prozessor daran gehin­dert werden, auf der Ancestor-Achse hinter der ihm genannten Grenze weiterzu­suchen.

Können innerhalb eines Kapitels <fussnote>-Container in verschiedenen Hie­rarchiestufen auftreten, und sollen diese dokumentbezogen global durchnum­meriert gezählt werden, so geht dies einfach über level="any".

Soll aber die Zählung in jedem Kapitel neu beginnen (per Default würden alle Fußnoten vor der aktuellen gezählt, auch die der Vorgängerkapitel), so muss das from-Attribut auf "kapitel" gesetzt werden:

<xsl:template match="fussnote">
  <xsl:number level="any" from="kapitel" format=".: "/>
  <xsl:value-of select="."/>
</xsl:template>

zählt die Fußnoten kapitelbezogen, und gibt deren Text aus:

1.: Die erste Fußnote dieses Kapitels

   

<< zurück vor >>
Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © Galileo Press, Bonn 2008
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT 2.0 & XPath 2.0 ― Das umfassende Handbuch" denselben Bestimmungen wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Galileo Press, Rheinwerkallee 4, 53227 Bonn