for-each-group der Muench-Methode zur Gruppierung vorziehen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

XSLT 1.0 enthielt keine ausdrückliche Unterstützung für die Gruppierung, sodass indirekte und potenziell verwirrende Techniken erfunden werden mussten.

Lösung

Machen Sie sich für Ihre Gruppierungsanforderungen die leistungsstarke Anweisung xsl:for-each-group zunutze! Diese Anweisung besitzt ein zwingend vorgeschriebenes select-Attribut, bei dem Sie einen Ausdruck angeben, der die Population der Knoten definiert, die Sie gruppieren wollen. Sie verwenden dann eines von vier Gruppierungskriterien, um die Population in Gruppen zu unterteilen. Diese werden als Nächstes erklärt. Bei der Verarbeitung der einzelnen Gruppen können Sie die Funktion current-group( ) einsetzen, um auf alle Knoten in der aktuellen Gruppe zuzugreifen. Sie benutzen die Funktion current-grouping-key( ), um auf den Wert des Schlüssels zuzugreifen, der die Gruppe definiert, die verarbeitet wird, wenn Sie nach dem Wert oder nach benachbarten Knoten gruppieren. Die Funktion current-grouping-key( ) hat keinen Wert, wenn nach dem Start- oder Endknoten gruppiert wird.

Sie können Gruppen auch sortieren, indem Sie eine oder mehrere xsl:sort-Anweisungen einfügen, um das Sortierkriterium zu definieren, genau wie Sie es bei xsl:for-each tun würden.

Nach Werten gruppieren (group-by="ausdruck")

Oft tritt ein klassisches Gruppierungsproblem auf, wenn Daten in Berichten verarbeitet werden. Denken Sie etwa an Verkaufsdaten. Produktmanager wollen Daten häufig nach der Verkaufsregion, nach dem Produkttyp oder dem Handelsvertreter gruppiert haben, je nachdem, welches Problem sie zu lösen versuchen. Sie verwenden das Attribut group-by, um einen Ausdruck zu definieren, der den oder die Werte ermittelt, die dafür sorgen, dass Knoten in der Population gruppiert werden. Beispielsweise würde group-by="@dept" dafür sorgen, dass Knoten mit dem gleichen dept-Wert gruppiert werden:

<xsl:template match="Employees">
  <EmployeesByDept>
    <xsl:for-each-group select="employee" group-by="@dept">
      <dept name="{current-grouping-key( )}">
        <xsl:copy-of select="current-group( )"/>
      </dept>
    </xsl:for-each-group>
  </EmployeesByDept>
</xsl:template>

Nach benachbarten Knoten gruppieren (group-adjacent="ausdruck")

In manchen Kontexten, wie etwa der Dokumentenverarbeitung, wollen Sie Knoten betrachten, die den gleichen Wert haben, vorausgesetzt, sie liegen auch noch nebeneinander. Wie group-by definiert auch group-adjacent einen Ausdruck, der verwendet wird, um den Wert zu ermitteln, der die Gruppierung veranlasst. Zwei Knoten, die einen solchen Wert besitzen, werden aber nur dann in die gleiche Gruppe gelangen, wenn sie in der Population nebeneinander liegen. Der Wert von group-adjacent darf nur ein Element haben; leere Sequenzen oder Sequenzen mit mehreren Werten verursachen einen Fehler.

Stellen Sie sich ein Dokument vor, das aus para-Elementen besteht, die mit heading-Elementen durchsetzt sind. Sie würden gern nur die para-Elemente extrahieren, ohne jedoch die Tatsache aus den Augen zu verlieren, dass einige Sequenzen von para-Elementen als Bestandteil desselben Themas zusammengehören:

<xsl:template match="doc">
  <xsl:copy>
    <xsl:for-each-group select="*" group-adjacent="name( )">
      <xsl:if test="self::para">
        <topic>
          <xsl:copy-of select="current-group( )"/>
        </topic>
      </xsl:if>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

Nach Startknoten gruppieren (group-starting-with="muster")

Oft ist, besonders in der Dokumentenverarbeitung, eine Gruppe von verwandten Knoten durch einen bestimmten Knoten abgegrenzt, etwa durch einen Titel, einen Untertitel oder eine andere Art von Überschriftenknoten. Die Gruppierung nach Startknoten macht es einfach, diese lose strukturierten Dokumente zu verarbeiten. Das Attribut group-starting-with definiert ein Muster, das solche Knoten in der Population erfasst, bei denen es sich um die Startknoten der Gruppe handelt. Dies ist vergleichbar den Mustern, die Sie mit dem match-Attribut in xsl:template-Anweisungen verwenden. Wenn das Muster einen Knoten in der Population filtert, sind alle nachfolgenden Knoten Teil der Gruppe, bis ein anderer Treffer gelandet wird. Der erste Knoten in der Population definiert eine Gruppe, ob er erfasst wird oder nicht. Dies impliziert, dass die Population wenigstens eine Gruppe enthält, nämlich die gesamte Population, selbst wenn das Muster niemals gefiltert wird.

Ein klassisches Beispiel ist das Wiederherstellen der Struktur aus einem unstrukturierten Dokument. XHTML ist ein gutes Beispiel für eine lose strukturierte Auszeichnungssprache, vor allem in Bezug auf die Verwendung von Überschriftenelementen (h1, h2 usw.). Die folgende Transformation stellt wieder eine gewisse Struktur her, indem die einzelnen Gruppen, die durch das Startelement h1 gekennzeichnet sind, in ein div-Element geschachtelt werden:

<xsl:template match="body">
  <xsl:copy>
    <xsl:for-each-group select="*" group-starting-with="h1">
      <div>
        <xsl:apply-templates select="current-group( )"/>
      </div>
    </xsl:for-each-group>
  </xsl:copy>
</xsl:template>

Nach Endknoten gruppieren (group-ending-with="muster")

Diese Form der Gruppierung ist vergleichbar mit group-starting-with, nutzt allerdings das Muster group-ending-with, um den letzten Knoten zu definieren, der in der aktuellen Gruppe sein soll. Der erste Knoten in der Population startet eine neue Gruppe, sodass es immer wenigstens eine Gruppe gibt, auch wenn das Muster selbst keine Knoten filtert.

Für die Gruppierung nach Endknoten gibt es unter allen Gruppierungsmethoden die wenigsten Anwendungen. Dies liegt daran, dass Dokumente, die für den Gebrauch durch Menschen gedacht sind, für einzelne neue Gruppen führende Elemente wie Überschriften verwenden und keine abschließenden. Im Buch XSLT 2.0 Programmer's Reference bietet Michael Kay ein Beispiel für eine Reihe von Dokumenten, die zum Zwecke der Übertragung in einzelne Teile untergeteilt wurden. In diesem Beispiel sind die Dokumentgrenzen durch das Fehlen eines Attributs continued='yes' gekennzeichnet. Ein etwas wahrscheinlicheres Beispiel ist eines, bei dem Sie ein einfaches Dokument mit Struktur versehen wollen, indem Sie Elemente auf der Grundlage eines Kriteriums zusammenfassen, das das Ende eines Stücks markiert. Beispielsweise können Sie mit folgendem Code Absätze in Abschnitte aus fünf Absätzen gruppieren:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:template match="doc">
    <xsl:copy>
      <xsl:for-each-group select="para" group-ending-with="para[position( ) mod 5 eq 0]">
        <section>
          <xsl:for-each select="current-group( )">
            <xsl:copy>
              <xsl:apply-templates select="@*|node( )"/>
            </xsl:copy>
          </xsl:for-each>
        </section>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="@* | node( )">
    <xsl:copy>
      <xsl:apply-templates select="@* | node( )"/>
    </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

Diskussion

Die Muench-Methode, benannt nach Steve Muench von Oracle, stellte eine innovative Möglichkeit dar, Daten in XSLT 1.0 zu gruppieren. Sie nutzte die XSLT-Fähigkeit aus, Dokumente mit Hilfe eines Schlüssels zu indizieren. Dabei wird der Index verwendet, um effizient die Menge der eindeutigen Gruppierungsschlüssel zu ermitteln. Diese Menge wird dann benutzt, um alle Knoten in der Gruppe zu verarbeiten:

<xsl:key name="products-by-category" select="product" use="@category"/>
<xsl:template match="/">
  <xsl:for-each select="//product[count(. | key('products-by-category', @category)[1])= 1]">
    <xsl:variable name="current-grouping-key" select="@category"/>
    <xsl:variable name="current-group" select="key('current-grouping-key', $current-grouping-key)"/>
    <xsl:for-each select="$current-group/*">
      <!-- processing for elements in group -->
      <!-- you can use xsl:sort here also, if necessary -->
    </xsl:for-each/>
  </xsl:for-each/>
</xsl:template>

Die Muench-Methode funktioniert zwar auch in 2.0 noch, allerdings sollten Sie for-each-group vorziehen, da dies wahrscheinlich mindestens genauso effizient ist, vermutlich sogar effizienter. Und was genauso wichtig ist: Dadurch wird Ihr Code verständlicher, vor allem für XSLT-Neulinge. Darüber hinaus benutzen Sie die gleiche grundlegende Anweisung, um Zugriff auf die vier unterschiedlichen Gruppierungsmöglichkeiten zu erlangen. Die Muench-Methode kann nur für eine wertebasierte Gruppierung verwendet werden. Der wahrscheinlich einzige zwingende Grund, die Muench-Gruppierung auch in XSLT 2.0 einzusetzen, ist Abwärtskompatibilität zu XSLT 1.0.

  

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