Jens: XSLT-Sortierung / Zusammenfasssung nach Elementnamen

Hallo zusammen... weiss nicht weiter... vielleicht ein interessantes Problem auch für euch. :-)

Folgendes XML:

<bestellpos>
<kunde>Nobody</kunde>
<menge>1</menge>
<artikelnummer>132</artikelnummer>
</bestellpos>

<bestellpos>
<kunde>Someone</kunde>
<menge>1</menge>
<artikelnummer>132</artikelnummer>
</bestellpos>

<bestellpos>
<kunde>Nobody</kunde>
<menge>2</menge>
<artikelnummer>135</artikelnummer>
</bestellpos>

.
.
.

soll so aufbereitet werden :

Nobody
------

1x 132
2x 125

Someone
-------

1x 132

Sprich: Unter dem Kunden sollen die einzelnen Positionen aufgelistet werden. Das XML ist für diesen Fall gänzlich bescheuert aufgebaut, kann aber NICHT geändert werden - sprich ich brauche eine XSLT-Lösung um die Daten entsprechend zu sortieren und zusammenzufassen.

Weiss jemand Rat?

Danke,

Jens.

  1. hallo jens,

    das ist wirklich ein interessantes problem. ich haette
    bestimmt viel spass es zu loesen, solche tuefteleien
    finde ich immer wieder aufregend, du auch? schicke mir
    die komplette xml-datei und einen scheck :-) , ich werde
    mich mal bei gelegenheit dieses problems annehmen. man
    liest sich, schoene gruesse vom bodensee,

    michael

    ss:) zu:) ls:& fo:) de:> va:) ch:? n4:& rl:? br:< js:| ie:% fl:{ mo:|

  2. hallo jens,

    nochmal ich: http://www.informatik.hu-berlin.de/~obecker/Lehre/SS2002/XML/07g-xslt.html
    ich hoffe das hilft dir auch, erneute gruesse,

    michael

    ss:) zu:) ls:& fo:) de:> va:) ch:? n4:& rl:? br:< js:| ie:% fl:{ mo:|

  3. Hallo,

    Sprich: Unter dem Kunden sollen die einzelnen Positionen aufgelistet werden. Das XML ist für diesen Fall gänzlich bescheuert aufgebaut, kann aber NICHT geändert werden - sprich ich brauche eine XSLT-Lösung um die Daten entsprechend zu sortieren und zusammenzufassen.

    Weiss jemand Rat?

    Ich habe in den letzen zwei wochen einige solche Fragen beantwortet. Was du möchtest ist ene Gruppierung auf Grund von Elementwerten, dazu kannst du einen key verwenden (xsl:key und key() ).
    Beispile und Lösungen findest du alle im Archiv (eine Suche nach meinem Namen und xsl:key sollte die entsprechenden Beiträge auflisten) . Siehe sie dir bitte an und wenn du dann noch Fragen hast, stelle sie in diesem Thread.

    Grüße
    Thomas

    1. Hallo Thomas,

      Sprich: Unter dem Kunden sollen die einzelnen Positionen aufgelistet werden. Das XML ist für diesen Fall gänzlich bescheuert aufgebaut, kann aber NICHT geändert werden - sprich ich brauche eine XSLT-Lösung um die Daten entsprechend zu sortieren und zusammenzufassen.

      Weiss jemand Rat?

      Ich habe in den letzen zwei wochen einige solche Fragen beantwortet. Was du möchtest ist ene Gruppierung auf Grund von Elementwerten, dazu kannst du einen key verwenden (xsl:key und key() ).
      Beispile und Lösungen findest du alle im Archiv (eine Suche nach meinem Namen und xsl:key sollte die entsprechenden Beiträge auflisten) . Siehe sie dir bitte an und wenn du dann noch Fragen hast, stelle sie in diesem Thread.

      ich bin gut voran gekommen. Jetzt habe ich allerdings noch ein Problem:

      Mein bisheriger Quellcode:

      <xsl:key name="folgenr" match="Table" use="folgenr"/>
      <xsl:key name="zuordnung" match="Table" use="zuordnung"/>
      <xsl:template match="/">
      <table class="h1" border="0">
      <tr><td></td><td></td>

      <xsl:for-each select="//Table[generate-id(.) = generate-id(key('folgenr',folgenr))]">
      <td><xsl:value-of select="artikelgruppe"/><br/><xsl:value-of select="artikel"/></td>
      <xsl:for-each select="key('folgenr', folgenr)">
      </xsl:for-each>
      </xsl:for-each>
      </tr>
      <xsl:for-each select="//Table[generate-id(.) = generate-id(key('zuordnung',zuordnung))]">
      <tr>
      <td><xsl:value-of select="./zuordnung"/></td>
      <td><xsl:value-of select="./faktor"/></td>
      <xsl:for-each select="key('zuordnung', zuordnung)">
      <td align="right">
      <table class="h1"><tr><td><xsl:value-of select="./anzahl"/></td><td><p align="right"><xsl:value-of select="./menge"/></p></td></tr></table></td>
      </xsl:for-each>
      </tr>
      </xsl:for-each>
      <tr><td>Gesamtsumme:</td><td></td>
      <xsl:for-each select="//Table[generate-id(.) = generate-id(key('folgenr',folgenr))]">
      <xsl:for-each select="key('folgenr', folgenr)">
      <xsl:if test="position() = last()"><td align="right"><xsl:value-of select="gesamtmenge"/></td></xsl:if>
      </xsl:for-each>
      </xsl:for-each>
      </tr>
      </table>
      </xsl:template>

      Das ganze erstellt eine Tabelle, welche eine Bestellliste ist. Oben stehen die Artikelgruppen- und Artikelbezeichnungen, auf der linken Seite stehen die Stellen, denen diese Artikel zugeordnet werden. Hauptbestandteil der Tabelle sind die Bestellfaktoren und die Bestellmenge (z.B. 90 x 2 = 180).

      Meine Frage: Wie könnte ich nun nach z.B. 10 Artikeln eine neue Tabelle anfangen, welche wieder 10 Artikel fast? Diese Bestelllisten sind ca. 100 Artikel in einer Reihe (bisher in Excel-Arbeitsblätter unterbegracht), was das ganze dann unübersichtlich macht...

      VIELEN DANK für deine immer wieder gute Hilfe und deine Ratschläge.

      Gruß,

      Jens.

      1. Hallo,

        Mein bisheriger Quellcode:

        Das läßt aber auf ein XML schließen das ein klein wenig ganz anders ist, als das XML was du als Bsp. gepostet hast. (??)
        Und es sind auch ein bischen zu viele for-each drinn ;-)

        Kannst du ein XML-Teil (so ein <Table>) posten?

        Meine Frage: Wie könnte ich nun nach z.B. 10 Artikeln eine neue Tabelle anfangen, welche wieder 10 Artikel fast? Diese Bestelllisten sind ca. 100 Artikel in einer Reihe (bisher in Excel-Arbeitsblätter unterbegracht), was das ganze dann unübersichtlich macht...

        Das wäre mit einem recursiven Template möglich (von 'fjh' gibt es so einen im Archiv öfters), aber dein Template muss dann komplett umgeschrieben werden.

        Wie gesagt, ein Stück vom echten XML wäre gut (es müssen nicht die echten Daten drinn stehen, die Struktur ist wichtig)

        VIELEN DANK für deine immer wieder gute Hilfe und deine Ratschläge.

        Ehm ... *hüstel* ... nun ja. Gerne ;-)

        Grüße
        Thomas

        1. Hallo,

          Mein bisheriger Quellcode:

          Das läßt aber auf ein XML schließen das ein klein wenig ganz anders ist, als das XML was du als Bsp. gepostet hast. (??)
          Und es sind auch ein bischen zu viele for-each drinn ;-)

          Jaja... :-)

          Das kommt davon, wenn man alles schnell zusammenfriemelt. :-)

          Kannst du ein XML-Teil (so ein <Table>) posten?

          Vorausgeschickt: Es ist wirklich übel. Es entbehrt jeder Logik XML-Dateien so aufzubauen, aber die Klassenbibliothek die das gegenüber verwendet und die diese Daten """aufbereitet""" ist auf dem Stand von 1995. :-)

          <Table>
          <folgenr><![CDATA[4]]></folgenr>
          <artikelgruppe><![CDATA[Bodenplatten]]></artikelgruppe>
          <artikel><![CDATA[2/o fbg. Sonderfarbe + Mattlack 690x990]]</artikel>
          <zuordnung><![CDATA[Sonstiges (jede TCH Filiale und Frische]]></zuordnung>
          <faktor><![CDATA[900]]></faktor>
          <anzahl><![CDATA[0]]></anzahl>
          <menge><![CDATA[0]]></menge>
          <gesamtmenge><![CDATA[0]]></gesamtmenge>
          </Table>

          <Table>
          <folgenr><![CDATA[4]]></folgenr>
          <artikelgruppe><![CDATA[Bodenplatten]]></artikelgruppe>
          <artikel><![CDATA[2/o fbg. Sonderfarbe + Mattlack 690x990]]></artikel>
          <zuordnung><![CDATA[FL UK (England)]]></zuordnung>
          <faktor><![CDATA[1]]></faktor>
          <anzahl><![CDATA[0]]></anzahl>
          <menge><![CDATA[0]]></menge>
          <gesamtmenge><![CDATA[0]]></gesamtmenge>
          </Table>

          Das wäre mit einem recursiven Template möglich (von 'fjh' gibt es so einen im Archiv öfters), aber dein Template muss dann komplett umgeschrieben werden.

          Mist.

          'ne Idee, wie ich das auf die schnelle am besten hinbekomme? Ich weiss nicht genau, wie ich innerhalb des key:-baums selektieren kann. Mit position() schlägt das immer fehl.

          Wie gesagt, ein Stück vom echten XML wäre gut (es müssen nicht die echten Daten drinn stehen, die Struktur ist wichtig)

          s.o. - es sind immer nur solche Elemente. Es ist leider keine Struktur drin, sonst wär's auch nicht so übel. :-)

          VIELEN DANK für deine immer wieder gute Hilfe und deine Ratschläge.
          Ehm ... *hüstel* ... nun ja. Gerne ;-)

          Guts Nächdle!

          Gruß,

          Jens.

          1. Hallo Jens,

            sorry für die späte Antwort:

            'ne Idee, wie ich das auf die schnelle am besten hinbekomme? Ich weiss nicht genau, wie ich innerhalb des key:-baums selektieren kann. Mit position() schlägt das immer fehl.

            Mit dem gewünschten Ausgabeform in der Tabelle (das ist das eigentliche Problem): nein, im Moment weiss ich es auch nicht (habe ein wenig zwar experimentiert, aber nicht wirklich Zeit dazu gehabt).
            Aber es ist wirklich eine sehr interssante Frage, so dass es mich jetzt auch "gepackt" hat die Antwort herauszufinden.

            Grüße
            Thomas

            1. Hallo Thomas,

              'ne Idee, wie ich das auf die schnelle am besten hinbekomme? Ich weiss nicht genau, wie ich innerhalb des key:-baums selektieren kann. Mit position() schlägt das immer fehl.

              Mit dem gewünschten Ausgabeform in der Tabelle (das ist das eigentliche Problem): nein, im Moment weiss ich es auch nicht (habe ein wenig zwar experimentiert, aber nicht wirklich Zeit dazu gehabt).
              Aber es ist wirklich eine sehr interssante Frage, so dass es mich jetzt auch "gepackt" hat die Antwort herauszufinden.

              Aber nur, wenn's dich wirklich gepackt hat. Das Problem ist gelöst. Werde nachher noch das XSLT posten, so als Lösungsvorschlag. :-)

              Liebe Grüße,

              Jens.

            2. Hallo Thomas,

              Mit dem gewünschten Ausgabeform in der Tabelle (das ist das eigentliche Problem): nein, im Moment weiss ich es auch nicht (habe ein wenig zwar experimentiert, aber nicht wirklich Zeit dazu gehabt).
              Aber es ist wirklich eine sehr interssante Frage, so dass es mich jetzt auch "gepackt" hat die Antwort herauszufinden.

              hier das versprochene XSLT. Wenn Dir was negativ auffältt, lass es mich wissen:

              <?xml version="1.0" encoding="UTF-8" ?>
              <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

              <!-- Anzahl Artikel pro Seite -->

              <xsl:variable name="posPerPage" select="10"/>

              <!-- Nach entsprechenden gleichen Elementen zusammengefasste Untermengen -->

              <xsl:key name="folgenr" match="Table" use="folgenr"/>
              <xsl:key name="zuordnung" match="Table" use="zuordnung"/>

              <!-- Haupttemplate -->

              <xsl:template match="/">
              <xsl:for-each select="//Table[generate-id(.) = generate-id(key('folgenr',folgenr))]">
               <xsl:if test="position() mod $posPerPage = 0 or position() = 1">
               xsl:choose
               <xsl:when test="position() = 1">
                <xsl:call-template name="newTable">
                <xsl:with-param name="start" select="number(position())" />
                <xsl:with-param name="end" select="number(position() + $posPerPage -1 )" />
                </xsl:call-template>
               </xsl:when>
               xsl:otherwise
                <xsl:call-template name="newTable">
                <xsl:with-param name="start" select="number(position() +1)" />
                <xsl:with-param name="end" select="number((position()) +  $posPerPage)" />
                </xsl:call-template>
               </xsl:otherwise>
               </xsl:choose>
               </xsl:if>
              </xsl:for-each>
              </xsl:template>

              <!-- Template für einzelne Tabellen
                Uebergabeparameter: Start-Position, End-Position -->

              <xsl:template name="newTable">
              <xsl:param name="start" />
              <xsl:param name="end" />
              <br/>
              Artikel <xsl:value-of select="$start"/> bis <xsl:value-of select="$end"/><br/><br/>
              <!-- Link zur nächsten Tabelle -->

              <a name="{$start}"></a>
              <!-- <a href="#{$end+1}"><font class="h1">Nächste Seite</font></a> -->
              <!-- <br/><br/> -->
              <!-- Tabelle -->

              <table class="h1" cellspacing="0" cellpadding="1" border="1">
              <tr>
               <td valign="bottom">Artikel<br></br>Zuordnung</td><td>Fakt.</td>

              <!-- Artikel -->

              <xsl:for-each select="//Table[generate-id(.) = generate-id(key('folgenr',folgenr))]">
                <xsl:if test="position() >= $start">
                 <xsl:if test="position() <= $end">
                  <td width="{round(875 div ($posPerPage+1))}px" height="100px" align="center" valign="top"><xsl:value-of select="artikelgruppe"/><br/><br/><xsl:value-of select="artikel"/></td>
                 </xsl:if>
                </xsl:if>
               </xsl:for-each>
              </tr>

              <!-- Zuordnung und Faktor -->

              <xsl:for-each select="//Table[generate-id(.) = generate-id(key('zuordnung',zuordnung))]">
              <tr>
              <td width="125px" align="left"><xsl:value-of select="substring(zuordnung,1,30)"/></td>
              <td align="center"><xsl:value-of select="./faktor"/></td>

              <!-- Multiplikatoren und Mengen pro Zuordnung -->

              <xsl:for-each select="key('zuordnung', zuordnung)">
              <xsl:if test="position() >= $start">
               <xsl:if test="position() <= $end">
                <td>
                 <table class="h1" width="100%">
                  <tr>
                  <td>
                   <table class="h1" width="100%" cellspacing="0" cellpadding="0">
                    <tr><td align="left"><xsl:value-of select="./anzahl"/></td></tr>
                   </table>
                  </td>
                  <td>
                   <table class="h1"  width="100%" cellspacing="0" cellpadding="0">
                   <tr><td align="right"> <xsl:value-of select="./menge"/></td></tr>
                   </table>
                  </td>
                  </tr>
                 </table>
                </td>
               </xsl:if>
              </xsl:if>
              </xsl:for-each>
              </tr>
              </xsl:for-each>

              <!-- Gesamtsumme pro Artikel -->

              <tr><td><b>Gesamtsumme</b></td><td align="center"><b>=</b></td>
              <xsl:for-each select="//Table[generate-id(.) = generate-id(key('folgenr',folgenr))]">
              <xsl:if test="position() >= $start">
               <xsl:if test="position() <= $end">
                <xsl:for-each select="key('folgenr', folgenr)">
                <xsl:if test="position() = last()">
                 <td align="right"><b><xsl:value-of select="gesamtmenge"/></b></td>
                </xsl:if>
                </xsl:for-each>
               </xsl:if>
              </xsl:if>
              </xsl:for-each>
              </tr>
              </table>
              </xsl:template>
              </xsl:stylesheet>

              1. Hallo Jens,

                hier das versprochene XSLT. Wenn Dir was negativ auffältt, lass es mich wissen:

                vorneweg: es ist eine gute Lösung.
                Das Problem wobei in anstehe - auf Grund, dass ich dein XML und die zu erwartenden Möglichkeiten nicht kenne - ist:
                deine Lösung funktioniert unter zwei Voraussetzungen:

                1. alle Gruppen in der key('folgenr') haben die selbe anzahl von Table-Elementen
                2. alle Table-Elemente in dieser Gruppe haben die selben Zuordnungen

                d.h. wenn deine Tabelle so aussieht:

                .   A   B   C
                -------------
                1   x   x   x
                -------------
                2   x   x   x
                -------------
                3   x   x   x
                -------------

                Aber wenn deine Tabelle Lücken enthält:
                .   A   B   C
                -------------
                1   x   x   x
                -------------
                2   x   x
                -------------
                3           x
                -------------

                Funktioniert die Lösung nicht mehr. Da rutscht z.B. die <td> vom C3 an die stelle von A3.

                Man nimm sich am Wo.End immerwas vor und es kommt just anderes ;-) und in der Arbeit habe ich eben zu arbeiten ;-) so dass ich wirklich keine Zeit hatte zum Grübeln.
                Dein XSL habe ich nur ein wenig vereinfacht, aber im grunde nichts geändert:

                Grüße
                Thomas

                <?xml version="1.0" encoding="iso-8859-1"?>
                <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
                <xsl:variable name="posPerPage" select="3"/>
                <xsl:key name="folgenr" match="Table" use="folgenr"/>
                <xsl:key name="zuordnung" match="Table" use="zuordnung"/>
                <xsl:template match="/">
                 <html>
                 <head>
                  <title>Untitled</title>
                 </head>
                 <body>
                 <xsl:apply-templates  />
                 </body>
                 </html>
                </xsl:template>
                <xsl:template match="data">
                 <xsl:for-each select="Table[generate-id(.) = generate-id(key('folgenr',folgenr)[1])]">
                  <xsl:if test="position() mod $posPerPage = 0 or position() = 1">
                   xsl:choose
                    <xsl:when test="position() = 1">
                     <xsl:call-template name="newTable">
                      <xsl:with-param name="start" select="number(position())" />
                      <xsl:with-param name="end" select="number(position() + $posPerPage -1 )" />
                     </xsl:call-template>
                    </xsl:when>
                    xsl:otherwise
                     <xsl:call-template name="newTable">
                      <xsl:with-param name="start" select="number(position() +1)" />
                      <xsl:with-param name="end" select="number((position()) +  $posPerPage)" />
                     </xsl:call-template>
                    </xsl:otherwise>
                   </xsl:choose>
                  </xsl:if>
                 </xsl:for-each>
                </xsl:template>
                <!-- Template für einzelne Tabellen
                  Uebergabeparameter: Start-Position, End-Position -->

                <xsl:template name="newTable">
                <xsl:param name="start" />
                <xsl:param name="end" />
                <br/>
                Artikel <xsl:value-of select="$start"/> bis <xsl:value-of select="$end"/><br/><br/>
                <!-- Link zur nächsten Tabelle -->
                <a name="{$start}"></a>
                <!-- <a href="#{$end+1}"><font class="h1">Nächste Seite</font></a> -->
                <!-- <br/><br/> -->
                <!-- Tabelle -->
                <table class="h1" cellspacing="0" cellpadding="1" border="1">
                 <tr>
                  <td valign="bottom">Artikel<br></br>Zuordnung</td><td>Fakt.</td>
                  <!-- Artikel -->
                  <xsl:for-each select="//Table[generate-id(.) = generate-id(key('folgenr',folgenr)[1])]">
                   <xsl:if test="(position() >= $start) and (position() <= $end)">
                    <td width="{round(875 div ($posPerPage+1))}px" height="100px" align="center" valign="top"><xsl:value-of select="artikelgruppe"/><br/><br/><xsl:value-of select="artikel"/></td>
                   </xsl:if>
                  </xsl:for-each>
                 </tr>

                <!-- Zuordnung und Faktor -->
                 <xsl:for-each select="//Table[generate-id(.) = generate-id(key('zuordnung',zuordnung)[1])]">
                  <tr>
                   <td width="125px" align="left"><xsl:value-of select="substring(zuordnung,1,30)"/></td>
                   <td align="center"><xsl:value-of select="faktor"/></td>
                  <!-- Multiplikatoren und Mengen pro Zuordnung -->
                   <xsl:for-each select="key('zuordnung', zuordnung)">
                    <xsl:if test="(position() >= $start) and (position() <= $end)">
                     <td>
                      <table class="h1" width="100%">
                      <tr>
                       <td>
                        <table class="h1" width="100%" cellspacing="0" cellpadding="0">
                         <tr><td align="left"><xsl:value-of select="anzahl"/></td></tr>
                        </table>
                       </td>
                       <td>
                        <table class="h1"  width="100%" cellspacing="0" cellpadding="0">
                         <tr><td align="right"> <xsl:value-of select="menge"/></td></tr>
                        </table>
                       </td>
                      </tr>
                      </table>
                     </td>
                    </xsl:if>
                   </xsl:for-each>
                  </tr>
                 </xsl:for-each>
                 <!-- Gesamtsumme pro Artikel -->
                 <tr><td><b>Gesamtsumme</b></td><td align="center"><b>=</b></td>
                 <xsl:for-each select="//Table[generate-id(.) = generate-id(key('folgenr',folgenr))]">
                  <xsl:if test="(position() >= $start) and (position() <= $end) and (key('folgenr', folgenr)[position() = last()])">
                   <td align="right"><b><xsl:value-of select="gesamtmenge"/></b></td>
                  </xsl:if>
                 </xsl:for-each>
                 </tr>
                </table>
                </xsl:template>
                </xsl:stylesheet>