Rekursionen mit for-Ausdrücken eliminieren

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie wollen aus einer Eingangssequenz eine Ausgangssequenz ableiten, bei der jedes Objekt in der Ausgabe eine beliebig komplexe Funktion der Eingabe darstellt und die Größen der einzelnen Sequenzen nicht unbedingt gleich sind.

Lösung

XPath 1.0

Nicht ausführbar in 1.0. Verwenden Sie ein rekursives XSLT-Template.

XPath 2.0

Verwenden Sie den for-Ausdruck von XPath 2.0. Wir zeigen hier vier Fälle, die demonstrieren, wie der for-Ausdruck Sequenzen unterschiedlicher Eingangs- und Ausgangsgrößen aufeinander abbilden kann.

Aggregation

(: Summe der Quadrate. :)

sum(for $x in $numbers return $x * $x)

(: Durchschnittswert der Quadrate. :)

avg(for $x in $numbers return $x * $x)

Zuordnung

(: Bildet eine Sequenz von Wörtern in allen Absätzen auf eine Sequenz von Wortlängen ab. :)

for $x in //para/tokenize(., ' ') return string-length($x)

(: Bildet eine Sequenz von Wörtern in einem Absatz auf eine Sequenz von Wortlängen ab, und zwar für Wörter, die mehr als drei Buchstaben umfassen. :)

for $x in //para/tokenize(., ' ') return if (string-length($x) gt 3) then string-length($x) else ( )

(: Identisch mit dem oben Genannten, allerdings mit einer Bedingung in der Eingangssequenz. :)

for $x in //para/tokenize(., ' ')[string-length(.) gt 3] return string-length($x)

Generierung

(: Generiert eine Sequenz von Quadraten der ersten 100 ganzen Zahlen. :)

for $i in 1 to 100 return $i * $i

(: Generiert eine Sequenz von Quadraten in umgekehrter Reihenfolge. :)

for $i in 0 to 10 return (10 - $i) * (10 - $i)

Erweiterung

(: Bildet eine Sequenz von Absätzen auf eine duplizierte Sequenz von Absätzen ab. :)

for $x in //para return ($x, $x)

(: Dupliziert Wörter. :)

for $x in //para/tokenize(., ' ') return ($x, $x)

(: Bildet Wörter auf Wort, gefolgt von Wortlänge, ab. :)

for $x in //para/tokenize(., ' ') return ($x, string-length($x))

Zusammenführung

(: Für jeden Kunden wird eine ID ausgegeben sowie die Gesamtheit der Bestellungen des Kunden. :)

for $cust in doc('customer.xml')/*/customer return($cust/id/text(), sum(for $ord in doc('orders.xml')/*/order[custID eq $cust/id] return ($ord/total)))

Diskussion

Wie ich bereits im Rezept Bedingungscode mit if-Ausdrücken verkleinern angedeutet habe, könnte das Hinzufügen von Konstrukten zur Ablaufsteuerung zu einer Ausdruckssprache wie XPath auf den ersten Blick als seltsam oder gar irreführend angesehen werden. Sie werden Ihre Zweifel jedoch schnell überwinden, wenn Sie die befreiende Leistungsfähigkeit dieser XPath 2.0-Konstrukte näher kennenlernen. Dies gilt vor allen Dingen für den XPath 2.0-Ausdruck for.

Die Stärke des for-Ausdrucks wird offenbar, wenn man bedenkt, dass dieser eingesetzt werden kann, um viele komplizierte rekursive XSLT 1.0-Lösungen auf einen einzigen XPath 2.0-Ausdruck zu reduzieren. Betrachten Sie das Problem des Berechnens von Summen in XSLT 1.0. Wenn Sie lediglich eine einfache Summe benötigen, gibt es kein Problem, da dazu die in XPath 1.0 integrierte Summenfunktion ausreicht. Müssen Sie hingegen die Summe von Quadraten berechnen, sind Sie gezwungen, ein umfangreicheres, schwierigeres und weniger transparentes rekursives Template zu schreiben. Um genau zu sein, bestand ein großer Teil der ersten Ausgabe dieses Buches aus Rezepten für vorgefertigte Lösungen für diese rekursiven Verrenkungen. Mit XPath 2.0 wird aus einer Summe von Quadraten nichts weiter als sum(for $x in $numbers return $x * $x), wobei $numbers die Zahlenfolge enthält, die wir summieren wollen. Denken Sie an die Bäume, die ich hätte retten können, wenn es in XPath 1.0 diese Lösung bereits gegeben hätte!

Hinter dem for-Ausdruck verbirgt sich aber noch mehr. Sie sind nicht nur auf eine Iterationsvariable beschränkt. Mehrere Variablen können kombiniert werden, um verschachtelte Schleifen zu erzeugen, die Sequenzen aus zusammenhängenden Knoten in einem komplexen Dokument erzeugen.

(:Rückgabe einer Sequenz, die aus para-IDs und den IDs besteht, die diese para-Elemente referenzieren. :)

for $s in /*/section, $p in $s/para, $r in $p/ref return ($p/@id, $r)

Sie sollten beachten, dass der vorhergehende Ausdruck sich semantisch nicht von dem folgenden Ausdruck unterscheidet, er ist nur kompakter:

for $s in /*/section return for $p in $s/para return for $r in $p/ref return ($p/@id, $r)

Außerdem sollten Sie beachten, dass keine Notwendigkeit besteht, ein verschachteltes for zu verwenden, wenn die Sequenz, die Sie erzeugen, durch einen traditionellen Pfadausdruck eleganter repräsentiert werden kann.

(: Diese Anwendung von for ist einfach ein umständlicherer Ausdruck für /*/section/para/ref. :)

for $s in /*/section, $p in $s/para, $r in $p/ref return $r

Manchmal wollen Sie die Position der einzelnen Objekte in einer Sequenz wissen, wenn Sie sie verarbeiten. Die Funktion position() können Sie nicht wie in einem xsl:for-each einsetzen, da ein XPath-for-Ausdruck die Kontextposition nicht ändert. Allerdings erreichen Sie den gewünschten Effekt mit dem folgenden Ausdruck:

for $pos in 1 to count($sequenz), $objekt in $sequenz[$pos] return $objekt, $pos

  

<< zurück vor >>

 

 

 

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

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" 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.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de