Thomas J.S.: push processing und pull processing

Beitrag lesen

Hallo Gunnar,

Für solche Fälle empfiehlt sich Templates mit verschiedenen Modi anzulegen:

So geht das also mit Templates ...

In http://forum.de.selfhtml.org/archiv/2006/6/t131423/#m850630 hast du push processing und pull processing gegenübergestellt. Mir ist zweites (for-each) intuitiver. Ist das Geschmackssache?

Ist beides gleich effektiv? Also, geht alles, was überhaupt geht, in beiden Varianten?

Ist beides gleich effizient? Oder ist das Laufzeitverhalten der einen Variante besser als die der anderen, so dass eventuell je nach Problemstellung sogar mal das eine, mal das andere besser ist?

ad 1) apply-templates und for-each stellen schon Alternativen dar, so dass man in den meisten Fällen sagen kann, es ist eine Geschmacksache welche man verwendet. Aber "in den meisten Fällen" heisst eben nicht immer. for-each ist gedacht um durch eine Sequenz von Knoten zu iterieren und in dem Sinne ist es also eine Alternative zu apply-templates.

ad 2) for-each ist z.B. dann nützlich, wenn das Ausgangsdokument eine sehr strikte Struktur hat und das Ergebnisdokument auch dementsprechend struriert ist. D.h. wenn man sehr viel über die Struktur des Ausgangsdokuments weiß und das Ergebnis auch entsprechend sein soll.
Nehmen wir dein Beispiel:
Ausgangsdoc.:
rdf:RDF
 rdf:Description
  dc:title</dc:title>
  dc:description</dc:description>
 </rdf:Description>
 rdf:Description
  dc:title</dc:title>
  dc:description</dc:description>
 </rdf:Description>
</rdf:RDF>

Ergebnisdoc.:
<ol>
 <li>
  <a></a>
 </li>
</ol>
<dl>
 <dt></dt>
 <dd></dd>
</dl>

Hier ist es kein Problem mit for-each zu arbeiten, denn es gibt nur eine Reihen von rdf:Description-Elementen, die wiederum nur ein dc:titel- gefolgt von einem dc:description-Element enthalten.
Aber wenn das Dokument weniger strikt strukturiert ist, also Elemente in Beileibigen Reihenfolge auftreten können, wäre es ein Problem mit for-each zu arbeiten, es sei denn man will im Ergebnisdokument eine bestimmte Struktur erreichen.
Gruppierungen (vor allem in Verbingung mit der "Muenchian grouping)) in XSLT 1.0 brauchten z.B. (fast) immer for-each, um einen Ausgangsknoten zu haben bzw. die einzelenen Gruppenmitglieder abzuarbeiten (in XSLT 2.0 gilt dies teils auch, aber dort sind die Möglichkeiten viel größer).
Aber auch mit apply-templates sind Gruppierungen möglich:
http://aktuell.de.selfhtml.org/tippstricks/xml/gruppierung1/

for-each und value-of haben den Vorteil, dass man mit denen Knoten direkt ansteuern kann, man kann sie quasi an die gewünste Stelle hereinziehen. Im gegensatz dazu müssen beim apply-templates die entsprechenden Templates erst gesucht werden.

Der Push-Stil (auch pattern-matching oder "rule-based stylesheets" genannt) ist eine der grundlegende Charaktereigenschaften von XSLT.
Damit kann man für jede Art von Knoten Template-Regel erstellen, ohne sich groß um die Struktur gedanken machen zu müssen.
Das mache ich z.B. jetzt wo ich ein XML habe in dem einige Elemente andere in beliebigen Reihenfolge enthalten können und wo andere XML-Dateien mit XInclude eingefügt werden und ich ein Ergebnisdokument brauche das zwar alles einthält, aber noch immer dem Schema entspricht. Ich habe da für alle Elemente Templates geschreiben und durch apply-templates lasse ich dann einfach diese Templates instanziiern. Das wäre mit for-each nicht möglich.

Aber nehmen wir ein Beispiel:

<cd>
   <titel>...
   <interpret>...
   <label>...
   <preis>...
</cd>

<xsl:template match="cd">
  <tr>
   <xsl:apply-templates />
  </tr>
 </xsl:template>
 <xsl:template match="titel | interpret | label | preis">
  <td><xsl:value-of select="."/></td>
 </xsl:template>

Dies geht so lange gut, bis alles CDs jeweils ein titel | interpret | label | preis-Element enthalten. Wenn dies aber nicht der Fall ist? Dann geht schon die Tabelle flöten.
Also eine Mischung:

<xsl:template match="cd">
  <tr>
   <td><xsl:value-of select="title"/></td>
   <td><xsl:value-of select="interpret"/></td>
   <td><xsl:value-of select="label"/></td>
   <td><xsl:value-of select="preis"/></td>
  </tr>
 </xsl:template>
Dies hat nicht nur den Vorteil, dass die Tabelle korrekt erstellt wird, sondern auch, dass ich hier genau kontrollieren kann, welche Elemente in welcher Reihenfolge ausgegeben werden.
Natürlich wäre hier auch ein:
   <xsl:for-each select="cd">
  <tr>
   <td><xsl:value-of select="title"/></td>
   <td><xsl:value-of select="interpret"/></td>
   <td><xsl:value-of select="label"/></td>
   <td><xsl:value-of select="preis"/></td>
  </tr>
 </xsl:for-each>
denkbar.

Was aber beim for-each oft vergessen wird, dass man damit den Konktextknoten wechselt (das dies ein sehr oft begangener Fehler ist belegt sogar auch das Archiv hier)
Dann kommt sowas heraus:
<xsl:for-each select="cd">
  <tr>
   <td><xsl:value-of select="cd/title"/></td>
                        ...

ad 3) Die Effizienz: for-each "kann" etwas besser sein, denn es vermeidet, dass für ein Elemente erst ein Template gesucht und dann ausgeführt werden muss, aber dieser Effekt dürfte sich erst bei _wirklich_ großen Dokumenten messbar auswirken.
Desweiteren macht for-each in machen Fällen das Stylesheet leichter verständlich:

<cd titel="..." interpret="..." label="..." preis="..." />

Da ist ein (vorausgesetzt, mann will alle Attribute als Elemente im Ergebnis haben):
 <xsl:for-each select="cd">
  <cd>
  <xsl:for-each select="@*">
   <xsl:element name="{name()}">
    <xsl:value-of select="."/>
   </xsl:element>
  </xsl:for-each>
  </cd>
 </xsl:for-each>

ebenso effizient wie ein:
 <xsl:template match="cd">
  <cd>
   <xsl:apply-templates />
  </cd>
 </xsl:template>
 <xsl:template match="cd/@*">
  <xsl:element name="{name()}">
   <xsl:value-of select="."/>
  </xsl:element>
 </xsl:template>

trotzdem ist erstere leichter zu lesen.

In den meisten Fällen ist es immer eine Mischung aus den beiden (push u. pull) Stilen die man verwendet. Es ist aber nicht nur Geschmacksache oder Stilfrage, sondern auch Erfahrung wie diese Mischung letztlich aussieht, da manchen mit for-each, maches mit apply-templates einfacher zu erreichen ist.

Grüße
Thomas