C#.NET * C++ * JAVASCRIPT * PYTHON * DIVERSES
XML * XML-SCHEMA * XPATH * XSL * XSL-FO * SVG * XQUERY * XPROC * ANT



XSL / Die XSLT - Struktur / xsl:for-each-group / Gruppieren mit group-starting-with und group-ending-with

Gruppieren mit group-starting-with und group-ending-with

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


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






Vielen Dank für Ihr Interesse an meiner Arbeit.



V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de

www.wilfried-grupe.de/XSL_for_each_group3.html