XSL / Die XSLT - Struktur / xsl:for-each-group / Gruppieren mit group-starting-with und group-ending-with
![]() |
![]() |
➪ Die Arbeit mit group-starting-with und group-ending-with dient beispielsweise dazu, mäßig strukturierten XML-Dokumenten nachträglich eine übersichtlichere Struktur verleihen. Dabei wird jedes Mal eine neue Gruppe gestartet, wenn das in group-starting-with definierte Start-Muster zutrifft; eine Gruppe wird geschlossen, wenn das in group-ending-with definierte End-Muster zutrifft.
Was einfach klingen mag, kann in der Realität etwas komplexer werden. Das möchte ich in folgendem Beispiel erläutern. Gegeben sei das folgende XML-Input-Dokument, bei dem sich eine Struktur lediglich aus der Reihenfolge der Elemente ergibt. Zum Beispiel beginnt eine Kauf-Gruppe mit idMensch, sie endet mit Gesamt. Eine Mensch-Gruppe beginnt mit id, wenn das übernächste Element vorname heißt, sonst ist es eine Ort-Gruppe.
<?xml version="1.0" standalone="yes"?>
<ROOT_SEQ>
<id>1</id>
<name>Neustadt</name>
<id>1</id>
<name>Holzflos</name>
<vorname>Hugo</vorname>
<Gehalt>234.56</Gehalt>
<idOrt>1</idOrt>
<idMensch>1</idMensch>
<anzahl>3</anzahl>
<bez>Hemd</bez>
<preis>12.99</preis>
<Gesamt>38.97</Gesamt>
<idMensch>1</idMensch>
<anzahl>9</anzahl>
<bez>Hemd</bez>
<preis>12.99</preis>
<Gesamt>116.91</Gesamt>
<idMensch>1</idMensch>
<anzahl>8</anzahl>
<bez>Hemd</bez>
<preis>12.99</preis>
<Gesamt>103.92</Gesamt>
<idMensch>1</idMensch>
<anzahl>9</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>233.91</Gesamt>
<idMensch>1</idMensch>
<anzahl>9</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>233.91</Gesamt>
<idMensch>1</idMensch>
<anzahl>8</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>207.92</Gesamt>
<idMensch>1</idMensch>
<anzahl>8</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>207.92</Gesamt>
<idMensch>1</idMensch>
<anzahl>8</anzahl>
<bez>Schuhe</bez>
<preis>151.23</preis>
<Gesamt>1209.84</Gesamt>
<idMensch>1</idMensch>
<anzahl>8</anzahl>
<bez>Schuhe</bez>
<preis>151.23</preis>
<Gesamt>1209.84</Gesamt>
<id>4</id>
<name>Nixlos</name>
<vorname>Nicole</vorname>
<Gehalt>1234.56</Gehalt>
<idOrt>1</idOrt>
<idMensch>4</idMensch>
<anzahl>8</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>207.92</Gesamt>
<idMensch>4</idMensch>
<anzahl>7</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>181.92999999999998</Gesamt>
<idMensch>4</idMensch>
<anzahl>6</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>155.94</Gesamt>
<idMensch>4</idMensch>
<anzahl>5</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>129.95</Gesamt>
<idMensch>4</idMensch>
<anzahl>4</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>103.96</Gesamt>
<idMensch>4</idMensch>
<anzahl>4</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>103.96</Gesamt>
<idMensch>4</idMensch>
<anzahl>3</anzahl>
<bez>Hose</bez>
<preis>25.99</preis>
<Gesamt>77.97</Gesamt>
<id>9</id>
<name>Sprachlos</name>
<vorname>Stefan</vorname>
<Gehalt>5430</Gehalt>
<idOrt>1</idOrt>
<idMensch>9</idMensch>
<anzahl>11</anzahl>
<bez>Hemd</bez>
<preis>12.99</preis>
<Gesamt>142.89000000000002</Gesamt>
<idMensch>9</idMensch>
<anzahl>22</anzahl>
<bez>Hemd</bez>
<preis>12.99</preis>
<Gesamt>285.78000000000003</Gesamt>
</ROOT_SEQ>
Wie Sie sehen, wird für jedes id-Element im Input ein Ort-Element im Output gebildet, sofern das übernächste Element ebenfalls id heißt. Daher wird hier auch mit following-sibling gearbeitet.
Nun startet innerhalb der aktuellen Gruppierung eine weitere, sie fragt, ob das schließende Element idOrt heisst. Damit wird die Gruppe geschlossen, das folgende Element ist dann schon idMensch. Um von hier aus an die Informationen der soeben geschlossenen Gruppe heranzukommen, arbeiten Sie mit preceding-sibling.
<root>
<xsl:for-each-group
select="/ROOT_SEQ/child::*"
group-starting-with="id[following-sibling::*
[position()=2]/local-name()='id']">
<xsl:variable name="vidort" select="." />
<Ort name="{following-sibling::*[position()=1]}" id="{$vidort}">
<xsl:for-each-group select="current-group()"
group-ending-with="idOrt">
<xsl:if test="preceding-sibling::*[position()=1] = $vidort">
<xsl:variable name="vidMensch"
select="preceding-sibling::*[position()=5]" />
<Mensch locname="{local-name()}"
idort="{preceding-sibling::*[position()=1]}"
id="{$vidMensch}"
vorname="{preceding-sibling::*[position()=3]}"
name="{preceding-sibling::*[position()=4]}">
</Mensch>
</xsl:if>
</xsl:for-each-group>
</Ort>
</xsl:for-each-group>
</root>
Zur besseren Orientierung und zum Verständnis des Ergebnisses habe ich die Anweisung locname='{local-name()}' eingearbeitet. Das Ergebnis ist überschaubar:
<root>
<Ort name="Neustadt" id="1">
<Mensch locname="idMensch"
idort="1"
id="1"
vorname="Hugo"
name="Holzflos"/>
<Mensch locname="idMensch"
idort="1"
id="4"
vorname="Nicole"
name="Nixlos"/>
<Mensch locname="idMensch"
idort="1"
id="9"
vorname="Stefan"
name="Sprachlos"/>
</Ort>
</root>
Wenn Sie die gesamte Logik haben möchten, die auch die Kauf-Elemente samt Attributen anzeigt, bitte sehr:
<root>
<xsl:for-each-group
select="/ROOT_SEQ/child::*"
group-starting-with="id[following-sibling::*
[position()=2]/local-name()='id']">
<xsl:variable name="vidort" select="." />
<Ort name="{following-sibling::*[position()=1]}"
id="{$vidort}">
<xsl:for-each-group select="current-group()"
group-ending-with="idOrt">
<xsl:if test="preceding-sibling::*[position()=1] = $vidort">
<xsl:variable name="vidMensch"
select="preceding-sibling::*[position()=5]" />
<Mensch locname="{local-name()}"
idort="{preceding-sibling::*[position()=1]}"
id="{$vidMensch}"
vorname="{preceding-sibling::*[position()=3]}"
name="{preceding-sibling::*[position()=4]}">
<xsl:for-each-group
select="current-group()"
group-ending-with="Gesamt">
<xsl:if test="./text() = $vidMensch">
<Kauf locname="{local-name()}" idmensch="{.}">
<xsl:for-each select="current-group()">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:for-each>
</Kauf>
</xsl:if>
</xsl:for-each-group>
</Mensch>
</xsl:if>
</xsl:for-each-group>
</Ort>
</xsl:for-each-group>
</root>
wg / 10. August 2020
Fragen? Anmerkungen? Tipps?
Bitte nehmen Sie Kontakt zu mir auf.
V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn
☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de