Announcement

Collapse
No announcement yet.

Gruppieren und Sortieren

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Gruppieren und Sortieren

    Hallo,
    ich verarbeite XML-Daten, die aus einer Datenbank kommen. Funktioniert alles super. Nun wurde die XML-Schnittstelle erweitert und das XSL muss angepasst werden ... und ich habe noch keinen Lösungsansatz.

    Beispiel XML alte Schnittstelle:
    Code:
    <root>
      <Gruppe id="A">
        <Gruppe id="A_1">
          <Gruppe id="A_11">
            <Objekt sortierfeld="dddd">...</Objekt>
            <Objekt sortierfeld="hhhh">...</Objekt>
            <Objekt sortierfeld="aaaa">...</Objekt>
          </Gruppe>
        </Gruppe>
     </Gruppe>
     <Gruppe id="B">...</Gruppe>
     <Gruppe id="C">...</Gruppe>
    </root>
    Beispiel XSL:
    Code:
    <xsl:stylesheet
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="xs"
      version="2.0">
      
      <xsl:output indent="yes"/>
     
    <xsl:template match="/root">
      <root>
        <xsl:apply-templates select="Gruppe"/>
      </root>
    </xsl:template>
    	
    <xsl:template match="Gruppe">
      <Gruppe>
        <xsl:copy-of select="@id"/>
        <xsl:for-each select="Objekt">
          <xsl:sort select="@sortierfeld" order="ascending" data-type="text" />
            <xsl:copy-of select="."/>
        </xsl:for-each>
        <xsl:apply-templates select="Gruppe"/>
      </Gruppe>	
    </xsl:template>
    
    </xsl:stylesheet>
    Bei der Transformation wird die Gruppen-Struktur beibehalten und die Objekte werden neu sortiert.

    erzeugte XML-Datei:
    Code:
    <root>
      <Gruppe id="A">
        <Gruppe id="A_1">
          <Gruppe id="A_11">
            <Objekt sortierfeld="aaaa">...</Objekt>
            <Objekt sortierfeld="dddd">...</Objekt>
            <Objekt sortierfeld="hhhh">...</Objekt>
          </Gruppe>
        </Gruppe>
     </Gruppe>
     <Gruppe id="B">...</Gruppe>
     <Gruppe id="C">...</Gruppe>
    </root>
    Nun können Objekte auch Objekte beinhalten, die auf eine Gruppe referenzieren. Diese beinhalteten Objekte sollen der Gruppe zugeordnet und dort eingefügt werden. Falls die Gruppe noch nicht vorhanden ist, dann soll die Gruppe erzeugte und an der richtigen Stelle / Ebene eingeordnet werden.

    Beispiel XML-Datei neu:
    Code:
    <root>
      <Gruppe id="A">
        <Gruppe id="A_1">
          <Gruppe id="A_11">
            <Objekt sortierfeld="dddd">
              <Objekt sortierfeld="eeee"><Referenz id="A_A11"/></Objekt>
              <Objekt sortierfeld="ffff"><Referenz id="G"/></Objekt>
              <Objekt sortierfeld="gggg"><Referenz id="B"/></Objekt>
            </Objekt>
            <Objekt sortierfeld="hhhh">...</Objekt>
            <Objekt sortierfeld="aaaa">...</Objekt>
          </Gruppe>
        </Gruppe>
     </Gruppe>
     <Gruppe id="B">...</Gruppe>
     <Gruppe id="C">...</Gruppe>
    </root>
    herauskommen soll:
    Code:
    <root>
      <Gruppe id="A">
        <Gruppe id="A_1">
          <Gruppe id="A_11">
            <Objekt sortierfeld="aaaa">...</Objekt>
            <Objekt sortierfeld="dddd">...</Objekt>
            <Objekt sortierfeld="eeee">...</Objekt>
            <Objekt sortierfeld="hhhh">...</Objekt>
          </Gruppe>
        </Gruppe>
     </Gruppe>
     <Gruppe id="B">
       <Objekt sortierfeld="gggg">...</Objekt>
     </Gruppe>
     <Gruppe id="C">...</Gruppe>
     <Gruppe id="G">
       <Objekt sortierfeld="ffff">...</Objekt>
     </Gruppe>
    </root>
    Mir ist noch nicht ganz klar, wie ich an die Sache herangehen soll bzw. von wo aus man ansetzt.
    Hat jemand einen Vorschlag. Ich bin sehr dankbar für alle Vorschläge und Hinweise.
    Die id´s der unterschiedlichen Ebenen sind unterschiedlich lang d.h. erste Ebene ein Zeichen, zweite Ebene 3 Zeichen, dritte Ebene 4 Zeichen usw.

    Danke schon mal im voraus!

  • #2
    Um die referenzierten Objekte zu verarbeiten, lässt sich ein key benutzen:
    Code:
    <xsl:stylesheet
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="xs"
      version="2.0">
      
      <xsl:output indent="yes"/>
      <xsl:strip-space elements="*"/>
      
      <xsl:key name="k1" match="Objekt[Referenz]" use="Referenz/@id"/>
     
      <xsl:template match="/root">
        <root>
          <xsl:apply-templates select="Gruppe"/>
        </root>
      </xsl:template>
              
      <xsl:template match="Gruppe">
        <Gruppe>
          <xsl:copy-of select="@id"/>
          <xsl:apply-templates select="Objekt[not(Referenz)], key('k1', @id)">
            <xsl:sort select="@sortierfeld" order="ascending" data-type="text" />
          </xsl:apply-templates>
          <xsl:apply-templates select="Gruppe"/>
        </Gruppe>	
      </xsl:template>
      
      <xsl:template match="Objekt">
        <xsl:copy>
          <xsl:copy-of select="@*, node() except (Objekt[Referenz], Referenz)"/>
        </xsl:copy>
      </xsl:template>
    
    </xsl:stylesheet>
    Damit wird aus
    Code:
    <root>
      <Gruppe id="A">
        <Gruppe id="A_1">
          <Gruppe id="A_11">
            <Objekt sortierfeld="dddd">
              <Objekt sortierfeld="eeee"><Referenz id="A_11"/></Objekt>
              <Objekt sortierfeld="ffff"><Referenz id="G"/></Objekt>
              <Objekt sortierfeld="gggg"><Referenz id="B"/></Objekt>
            </Objekt>
            <Objekt sortierfeld="hhhh">...</Objekt>
            <Objekt sortierfeld="aaaa">...</Objekt>
          </Gruppe>
        </Gruppe>
     </Gruppe>
     <Gruppe id="B">...</Gruppe>
     <Gruppe id="C">...</Gruppe>
    </root>
    dann
    Code:
    <root>
       <Gruppe id="A">
          <Gruppe id="A_1">
             <Gruppe id="A_11">
                <Objekt sortierfeld="aaaa">...</Objekt>
                <Objekt sortierfeld="dddd"/>
                <Objekt sortierfeld="eeee"/>
                <Objekt sortierfeld="hhhh">...</Objekt>
             </Gruppe>
          </Gruppe>
       </Gruppe>
       <Gruppe id="B">
          <Objekt sortierfeld="gggg"/>
       </Gruppe>
       <Gruppe id="C"/>
    </root>
    Beachte, dass ich das XML-Dokument leicht verändert habe, aus
    Code:
    <Objekt sortierfeld="eeee"><Referenz id="A_A11"/></Objekt>
    habe ich
    Code:
    <Objekt sortierfeld="eeee"><Referenz id="A_11"/></Objekt>
    gemacht, sonst verweist die Referenz auf keine Gruppe.

    Comment


    • #3
      Hier ist eine Erweiterung des Stylesheet, das auch neue Gruppen erzeugen sollte:
      Code:
      <xsl:stylesheet
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema"
        exclude-result-prefixes="xs"
        version="2.0">
        
        <xsl:output indent="yes"/>
        <xsl:strip-space elements="*"/>
        
        <xsl:key name="k1" match="Objekt[Referenz]" use="Referenz/@id"/>
        <xsl:key name="k2" match="Objekt[Referenz]" use="string-length(Referenz/@id)"/>
        <xsl:key name="k3" match="Gruppe" use="@id"/>
       
        <xsl:template match="/root">
          <root>
            <xsl:apply-templates 
              select="Gruppe, 
                      key('k2', string-length(Gruppe[1]/@id))[not(key('k3', Referenz/@id))]">
              <xsl:sort select="(@id, Referenz/@id)[1]"/>
            </xsl:apply-templates>
          </root>
        </xsl:template>
                
        <xsl:template match="Gruppe">
          <Gruppe>
            <xsl:copy-of select="@id"/>
            <xsl:apply-templates select="Objekt[not(Referenz)], key('k1', @id)" mode="m1">
              <xsl:sort select="@sortierfeld" order="ascending" data-type="text" />
            </xsl:apply-templates>
            <xsl:apply-templates 
              select="Gruppe, 
                      key('k2', string-length(Gruppe[1]/@id))[not(key('k3', Referenz/@id))]">
              <xsl:sort select="(@id, Referenz/@id)[1]"/>
            </xsl:apply-templates>
          </Gruppe>	
        </xsl:template>
        
        <xsl:template match="Objekt">
          <Gruppe id="{Referenz/@id}">
            <xsl:apply-templates select="." mode="m1"/>
          </Gruppe>
        </xsl:template>
        
        <xsl:template match="Objekt" mode="m1">
          <xsl:copy>
            <xsl:copy-of select="@*, node() except (Objekt[Referenz], Referenz)"/>
          </xsl:copy>
        </xsl:template>
      
      </xsl:stylesheet>
      Ich habe das aber nicht mit mehr als deinem Beispiel XML getestet.

      Comment


      • #4
        Vielen Dank. Das funktioniert für Referenzen mit einstelliger id sehr gut. So habe ich es mir vorgestellt.
        Bei mehrstelligen id´s wird jedoch, wenn es diese Gruppe noch nicht gibt, leider keine Gruppe angelegt.
        Außerdem wird die Gruppe mehrfach angelegt, wenn es mehrere Referenzen mit der gleichen id gibt.
        Gibt es eine Möglichkeit dafür?
        Problem ist hier, wenn es eine Unterebene ist, die übergeordneten Ebenen zu suchen bzw. auch diese neu anzulegen.
        Hat jemand dafür einen Lösungsvorschlag?

        Comment


        • #5
          Wie viele verschachtelte Gruppen kann es geben, beliebig viele? Kann man das id-Format in etwas "programmatisch" handlicheres verändern wie a_1_1, so dass man dann per tokenize("a_1_1", "_") die Ids der einzelnen Ebenen finden kann?
          Ein längeres, komplexeres Beispieldokument könnte auch helfen, um den XSLT-Code zu verbessern, Dinge wie doppelt erzeugte Elemente fallen halt erst auf, wenn man mit der passenden Eingabe testet.
          Zuletzt editiert von Martin Honnen; 08.11.2010, 19:40.

          Comment


          • #6
            Es gibt zur Zeit 6 Gruppenebenen, aber im Grunde können es beliebig viele sein. Es könnten noch Untergruppen dazu kommen.
            Das id-Format kann nicht verändert werden. Es wäre aber möglich ein anderes Attribut zu erzeugen, welches eine veränderte Form der id beinhaltet.
            Die id ist zur Zeit wie folgt aufgebaut (ich hatte sie in meinem Beispieldokument ein wenig anders dargestellt):
            - erste Ebene sind R, S oder Z
            - zweite Ebene: Buchstabe von erster Ebene + Leerzeichen + Buchstabe/Zahl
            - jede weitere Unterebene besitzt einen Buchstaben / Zahl mehr
            z.B. "R", "R A", "R AG", "R AG1" ...

            Hier ein längeres und abgeändertes Beispieldokument:
            Code:
            <?xml version="1.0" encoding="UTF-8"?>
            <root>
            	<Gruppe id="R">
            		<Gruppe id="R A">
            			<Gruppe id="R AA">
            				<Produkt sortierfeld="z"/>
            			</Gruppe>
            			<Gruppe id="R AE">
            				<Produkt sortierfeld="z">
            					<Eigenschaften>
            						<Titel sortierfeld="a">
            							<Eigenschaften>
            								<Referenz id="R"/>
            							</Eigenschaften>
            						</Titel>
            						<Titel sortierfeld="b">
            							<Eigenschaften>
            								<Referenz id="R A"/>
            							</Eigenschaften>
            						</Titel>
            						<Titel sortierfeld="c">
            							<Eigenschaften>
            								<Referenz id="R B"/>
            							</Eigenschaften>
            						</Titel>
            						<Titel sortierfeld="d">
            							<Eigenschaften>
            								<Referenz id="R AE"/>
            							</Eigenschaften>
            						</Titel>
            						<Titel sortierfeld="e">
            							<Eigenschaften>
            								<Referenz id="R F"/>
            							</Eigenschaften>
            						</Titel>
            						<Titel sortierfeld="f">
            							<Eigenschaften>
            								<Referenz id="R FA"/>
            							</Eigenschaften>
            						</Titel>
            						<Titel sortierfeld="g">
            							<Eigenschaften>
            								<Referenz id="R F"/>
            							</Eigenschaften>
            						</Titel>
            						<Titel sortierfeld="h">
            							<Eigenschaften>
            								<Referenz id="R FG"/>
            							</Eigenschaften>
            						</Titel>
            						<Titel sortierfeld="h">
            							<Eigenschaften>
            								<Referenz id="R AA1"/>
            							</Eigenschaften>
            						</Titel>
            					</Eigenschaften>
            				</Produkt>
            			</Gruppe>
            		</Gruppe>
            	</Gruppe>
            </root>
            Das zugehörige XSL:
            Code:
            <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
            
            	<xsl:output indent="yes"/>
            	<xsl:strip-space elements="*"/>
            	
            	<xsl:key name="k1" match="Titel" use="Eigenschaften/Referenz/@id"/>
            	<xsl:key name="k2" match="Titel" use="string-length(Eigenschaften/Referenz/@id)"/>
            	<xsl:key name="k3" match="Gruppe" use="@id"/>
            
            	<xsl:template match="/root">
            		<root>
            			<xsl:apply-templates select="Gruppe, 
                            key('k2', string-length(Gruppe[1]/@id))[not(key('k3', Eigenschaften/Referenz/@id))]">
            				<xsl:sort select="(@id, Eigenschaften/Referenz/@id)[1]"/>
            			</xsl:apply-templates>
            		</root>
            	</xsl:template>
            	
            	<xsl:template match="Gruppe">
            		<Gruppe>
            			<xsl:copy-of select="@id"/>
            			<xsl:apply-templates select="Produkt, key('k1', @id)" mode="m1">
            				<xsl:sort select="@sortierfeld" order="ascending" data-type="text"/>
            			</xsl:apply-templates>
            			<xsl:apply-templates select="Gruppe, 
                            key('k2', string-length(Gruppe[1]/@id))[not(key('k3', Eigenschaften/Referenz/@id))]">
            				<xsl:sort select="(@id, Eigenschaften/Referenz/@id)[1]"/>
            			</xsl:apply-templates>
            		</Gruppe>
            	</xsl:template>
            	
            	<xsl:template match="Titel">
            		<Gruppe id="{Eigenschaften/Referenz/@id}">
            			<xsl:apply-templates select="." mode="m1"/>
            		</Gruppe>
            	</xsl:template>
            	
            	<xsl:template match="Produkt | Titel" mode="m1">
            		<Produkt>
            			<xsl:copy-of select="@*"/>
            		</Produkt>
            	</xsl:template>
            	
            </xsl:stylesheet>
            erzeugt wird:
            Code:
            <?xml version="1.0" encoding="UTF-8"?>
            <root>
            	<Gruppe id="R">
            		<Produkt sortierfeld="a"/>
            		<Gruppe id="R A">
            			<Produkt sortierfeld="b"/>
            			<Gruppe id="R AA">
            				<Produkt sortierfeld="z"/>
            			</Gruppe>
            			<Gruppe id="R AE">
            				<Produkt sortierfeld="d"/>
            				<Produkt sortierfeld="z"/>
            			</Gruppe>
            			<Gruppe id="R FA">
            				<Produkt sortierfeld="f"/>
            			</Gruppe>
            			<Gruppe id="R FG">
            				<Produkt sortierfeld="h"/>
            			</Gruppe>
            		</Gruppe>
            		<Gruppe id="R B">
            			<Produkt sortierfeld="c"/>
            		</Gruppe>
            		<Gruppe id="R F">
            			<Produkt sortierfeld="e"/>
            		</Gruppe>
            		<Gruppe id="R F">
            			<Produkt sortierfeld="g"/>
            		</Gruppe>
            	</Gruppe>
            </root>
            herauskommen soll:
            Code:
            <?xml version="1.0" encoding="UTF-8"?>
            <root>
            	<Gruppe id="R">
            		<Produkt sortierfeld="a"/>
            		<Gruppe id="R A">
            			<Produkt sortierfeld="b"/>
            			<Gruppe id="R AA">
            				<Produkt sortierfeld="z"/>
            				<Gruppe id="R AA1">
            					<Produkt sortierfeld="h"/>
            				</Gruppe>
            			</Gruppe>
            			<Gruppe id="R AE">
            				<Produkt sortierfeld="d"/>
            				<Produkt sortierfeld="z"/>
            			</Gruppe>
            		</Gruppe>
            		<Gruppe id="R B">
            			<Produkt sortierfeld="c"/>
            		</Gruppe>
            		<Gruppe id="R F">
            			<Produkt sortierfeld="e"/>
            			<Produkt sortierfeld="g"/>
            			<Gruppe id="R FA">
            				<Produkt sortierfeld="f"/>
            			</Gruppe>
            			<Gruppe id="R FG">
            				<Produkt sortierfeld="h"/>
            			</Gruppe>
            		</Gruppe>
            	</Gruppe>
            </root>
            Probleme gibt es bei Gruppen, die es noch nicht gibt. Die Gruppe "R F" wird 2x angelegt. Die Gruppen "R FA" und "R FG" werden falsch unter "R A" einsortiert, statt unter "R F". Außerdem wird die Gruppe "R AA1" nicht angelegt.
            Hat jemand eine Idee, wie man dieses Problem beheben könnte?
            Wie schon gesagt, bin ich für alle Vorschläge dankbar.

            Comment


            • #7
              Hat jemand einen Vorschlag? Oder kann man so etwas garnicht lösen?
              Man könnte auch eine zusätzliche Transformation zwischenschalten, wenn notwendig.

              Comment


              • #8
                Hier ist ein Ansatz, der erst versucht, alle Gruppen zu konstruieren und dann anschliessend per for-each-group Duplikate eliminiert:
                Code:
                <xsl:stylesheet
                  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                  xmlns:xs="http://www.w3.org/2001/XMLSchema"
                  xmlns:mf="http://example.com/mf"
                  exclude-result-prefixes="xs mf"
                  version="2.0">
                
                  <xsl:output indent="yes"/>
                  <xsl:strip-space elements="*"/>
                  
                  <xsl:function name="mf:distinct-and-sort" as="node()*">
                    <xsl:param name="groups" as="element(Gruppe)*"/>
                    <xsl:for-each-group select="$groups" group-by="@id">
                      <xsl:sort select="current-grouping-key()"/>
                      <Gruppe id="{current-grouping-key()}">
                        <xsl:for-each-group select="current-group()/Produkt" group-by="@sortierfeld">
                          <xsl:sort select="current-grouping-key()"/>
                          <Produkt sortierfeld="{current-grouping-key()}"/>
                        </xsl:for-each-group>
                        <xsl:sequence select="mf:distinct-and-sort(current-group()/Gruppe)"/>
                      </Gruppe>
                    </xsl:for-each-group>
                  </xsl:function>
                	
                  <xsl:key name="k1"
                    match="Titel"
                    use="for $length in (1 to string-length(Eigenschaften/Referenz/@id))
                         return substring(Eigenschaften/Referenz/@id, 1, $length)"/>
                  
                  <xsl:template match="root">
                    <xsl:variable name="all-groups" as="element(Gruppe)*">
                      <xsl:apply-templates
                        select="Gruppe, descendant::Titel[not(substring(Eigenschaften/Referenz/@id, 1, 1) = /Root/Gruppe/@id)]">
                          <xsl:with-param name="level" select="1"/>
                      </xsl:apply-templates> 
                    </xsl:variable>
                    <xsl:copy>
                      <xsl:sequence select="mf:distinct-and-sort($all-groups)"/>
                    </xsl:copy>
                  </xsl:template>
                	
                  <xsl:template match="Gruppe">
                    <xsl:variable name="level" as="xs:integer" select="count(ancestor::Gruppe) + 3"/>
                    <xsl:copy>
                      <xsl:copy-of select="@id"/>
                        <xsl:apply-templates select="Produkt" mode="leaf"/>
                      <xsl:apply-templates
                        select="Gruppe, key('k1', @id, current())[string-length(Eigenschaften/Referenz/@id) gt string-length(current()/@id)]">
                          <xsl:with-param name="level" select="$level"/>
                      </xsl:apply-templates>
                    </xsl:copy>
                  </xsl:template>
                	
                  <xsl:template match="Titel">
                    <xsl:param name="level" as="xs:integer"/>
                      <Gruppe id="{substring(Eigenschaften/Referenz/@id, 1, $level)}">
                        <xsl:variable name="next-level" as="xs:integer" select="if ($level eq 1) then 3 else $level + 1"/>
                        <xsl:choose>
                          <xsl:when test="string-length(Eigenschaften/Referenz/@id) ge $next-level">
                            <xsl:apply-templates select=".">
                              <xsl:with-param name="level" select="$next-level"/>
                            </xsl:apply-templates>
                          </xsl:when>
                          <xsl:otherwise>
                            <xsl:apply-templates select="." mode="leaf"/>
                          </xsl:otherwise>
                        </xsl:choose>
                      </Gruppe>
                  </xsl:template>
                	
                  <xsl:template match="Produkt | Titel" mode="leaf">
                    <Produkt>
                      <xsl:copy-of select="@*"/>
                    </Produkt>
                  </xsl:template>
                
                </xsl:stylesheet>

                Comment


                • #9
                  Vielen Dank für diesen Ansatz. Das Ergebnis sieht sehr vielversprechend aus.
                  Leider kann ich den Ansatz aufgrund der Komplexität nur schwer nachvollziehen und meine Optimierungsversuche waren größtenteils erfolglos

                  Eine Schwierigkeit ist nun auch noch dazu gekommen (wusste ich vorher nicht, sorry). Das Root-Element besitzt ein Attribut Ebenen mit dem Hinweis auf die Ebenentiefe. Z.B. 1011 bedeutet, dass es bis zu 3 verschachtelte Gruppen gibt. Besitzt ein Titel eine tiefer strukturierte Gruppe z.B. "R AA1" (4. Ebene), muss diese auf "R AA" reduziert werden und das Produkt dort eingeordnet werden.

                  Gibt es keine weitere Untergruppe, erzeugt die Datenbank künstliche Gruppen, welche wieder reduziert werden müssen. Dies ist mir glaube ich gelungen.

                  Produkte sollen nicht gruppiert werden. Ich habe versucht die Gruppierung zu entfernen. Leider ohne Erfolg.

                  Außerdem möchte ich gern die Gruppennamen ausgeben. Diese Ausgabe ist mir nur bei den Gruppen, nicht bei den Referenzen, gelungen.

                  Code:
                  <?xml version="1.0" encoding="UTF-8"?>
                  <root Ebenen="1011">
                  	<Gruppe id="R" Gruppenname="Gruppe1">
                  		<Gruppe id="R A" Gruppenname="Gruppe2">
                  			<Gruppe id="R AA"  Gruppenname="Gruppe3_1">
                  				<Produkt sortierfeld="x"/>
                  			</Gruppe>
                  			<Gruppe id="R AE" Gruppenname="Gruppe3_2">
                  				<Produkt sortierfeld="x">
                  					<Eigenschaften>
                  						<Titel sortierfeld="a">
                  							<Eigenschaften>
                  								<Referenz id="R A" Name="Gruppe2"/>
                  							</Eigenschaften>
                  						</Titel>
                  						<Titel sortierfeld="b">
                  							<Eigenschaften>
                  								<Referenz id="R AA" Name="Gruppe3_1"/>
                  							</Eigenschaften>
                  						</Titel>
                  						<Titel sortierfeld="c">
                  							<Eigenschaften>
                  								<Referenz id="R AA1" Name="Gruppe4"/>
                  							</Eigenschaften>
                  						</Titel>
                  						<Titel sortierfeld="c">
                  							<Eigenschaften>
                  								<Referenz id="R AA1" Name="Gruppe4"/>
                  							</Eigenschaften>
                  						</Titel>
                  						<Titel sortierfeld="a">
                  							<Eigenschaften>
                  								<Referenz id="R AG" Name="Gruppe3_3"/>
                  							</Eigenschaften>
                  						</Titel>
                  					</Eigenschaften>
                  				</Produkt>		
                  			</Gruppe>
                  		</Gruppe>
                  	</Gruppe>
                  	<Gruppe id="S" Gruppenname="Gruppe1">
                  		<Gruppe id="S A" Gruppenname="Gruppe2">
                  			<Gruppe id="S A"  Gruppenname="Gruppe3_1">
                  				<Produkt sortierfeld="x"/>
                  			</Gruppe>
                  		</Gruppe>
                  	</Gruppe>	
                  </root>
                  Code:
                  <xsl:stylesheet
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    xmlns:xs="http://www.w3.org/2001/XMLSchema"
                    xmlns:mf="http://example.com/mf"
                    exclude-result-prefixes="xs mf"
                    version="2.0">
                  
                    <xsl:output indent="yes"/>
                    <xsl:strip-space elements="*"/>
                    
                    <xsl:function name="mf:distinct-and-sort" as="node()*">
                      <xsl:param name="groups" as="element(Gruppe)*"/>
                      <xsl:for-each-group select="$groups" group-by="@id">
                        <xsl:sort select="current-grouping-key()"/>
                        <Gruppe id="{current-grouping-key()}">
                  		  <Gruppenname><xsl:value-of select="current-group()/@Gruppenname"/></Gruppenname>
                  		 <xsl:for-each-group select="current-group()/Produkt" group-by="@sortierfeld">
                            <xsl:sort select="current-grouping-key()"/>
                            <Produkt sortierfeld="{current-grouping-key()}"/>
                          </xsl:for-each-group>
                          <xsl:sequence select="mf:distinct-and-sort(current-group()/Gruppe)"/>
                        </Gruppe>
                      </xsl:for-each-group>
                    </xsl:function>
                  	
                    <xsl:key name="k1"
                      match="Titel"
                      use="for $length in (1 to string-length(Eigenschaften/Referenz/@id))
                           return substring(Eigenschaften/Referenz/@id, 1, $length)"/>
                    
                    <xsl:template match="root">
                      <xsl:variable name="all-groups" as="element(Gruppe)*">
                        <xsl:apply-templates
                          select="Gruppe, descendant::Titel[not(substring(Eigenschaften/Referenz/@id, 1, 1) = /Root/Gruppe/@id)]">
                            <xsl:with-param name="level" select="1"/>
                        </xsl:apply-templates> 
                      </xsl:variable>
                      <xsl:copy>
                        <xsl:sequence select="mf:distinct-and-sort($all-groups)"/>
                      </xsl:copy>
                    </xsl:template>
                  	
                    <xsl:template match="Gruppe">
                      <xsl:variable name="level" as="xs:integer" select="count(ancestor::Gruppe) + 3"/>
                  	<xsl:choose>
                  		<xsl:when test="parent::Gruppe/@id = @id">
                  			<xsl:apply-templates select="descendant::Produkt" mode="leaf"/>
                  		</xsl:when>
                  		<xsl:otherwise>
                  			<xsl:copy>
                  				  <xsl:copy-of select="@id | @Gruppenname"/>
                  					<xsl:apply-templates select="Produkt" mode="leaf"/>
                  				  <xsl:apply-templates
                  					select="Gruppe, key('k1', @id, current())[string-length(Eigenschaften/Referenz/@id) gt string-length(current()/@id)]">
                  					  <xsl:with-param name="level" select="$level"/>
                  				  </xsl:apply-templates>
                  				</xsl:copy>
                  		</xsl:otherwise>		
                  	</xsl:choose>   
                    </xsl:template>
                  	
                    <xsl:template match="Titel">
                      <xsl:param name="level" as="xs:integer"/>
                        <Gruppe id="{substring(Eigenschaften/Referenz/@id, 1, $level)}">
                          <xsl:variable name="next-level" as="xs:integer" select="if ($level eq 1) then 3 else $level + 1"/>
                          <xsl:choose>
                            <xsl:when test="string-length(Eigenschaften/Referenz/@id) ge $next-level">
                              <xsl:apply-templates select=".">
                                <xsl:with-param name="level" select="$next-level"/>
                              </xsl:apply-templates>
                            </xsl:when>
                            <xsl:otherwise>
                              <xsl:apply-templates select="." mode="leaf"/>
                            </xsl:otherwise>
                          </xsl:choose>
                        </Gruppe>
                    </xsl:template>
                  	
                    <xsl:template match="Produkt | Titel" mode="leaf">
                      <Produkt>
                        <xsl:copy-of select="@*"/>
                      </Produkt>
                    </xsl:template>
                  
                  </xsl:stylesheet>
                  gewünschtes Ergebnis:
                  Code:
                  <?xml version="1.0" encoding="UTF-8"?>
                  <root>
                  	<Gruppe id="R">
                  		<Gruppenname>Gruppe1</Gruppenname>
                  		<Gruppe id="R A">
                  			<Gruppenname>Gruppe2</Gruppenname>
                  			<Produkt sortierfeld="a"/>
                  			<Gruppe id="R AA">
                  				<Gruppenname>Gruppe3_1</Gruppenname>
                  				<Produkt sortierfeld="b"/>
                  				<Produkt sortierfeld="c"/>
                  				<Produkt sortierfeld="c"/>
                  				<Produkt sortierfeld="x"/>
                  			</Gruppe>
                  			<Gruppe id="R AE">
                  				<Gruppenname>Gruppe3_2</Gruppenname>
                  				<Produkt sortierfeld="x"/>
                  			</Gruppe>
                  			<Gruppe id="R AG">
                  				<Gruppenname>Gruppe3_3</Gruppenname>
                  				<Produkt sortierfeld="a"/>
                  			</Gruppe>
                  		</Gruppe>
                  	</Gruppe>
                  	<Gruppe id="S">
                  		<Gruppenname>Gruppe1</Gruppenname>
                  		<Gruppe id="S A">
                  			<Gruppenname>Gruppe2</Gruppenname>
                  			<Produkt sortierfeld="x"/>
                  		</Gruppe>
                  	</Gruppe>
                  </root>

                  Comment


                  • #10
                    Hat jemand einen Vorschlag?

                    Comment


                    • #11
                      Ich habe nochmal versucht den Ansatz zu verstehen und zu optimieren. Ich glaube, dass es mir auch gelungen ist.
                      Für Anmerkungen und Verbesserungsvorschläge bin ich immer dankbar.

                      XML-Beispiel:
                      Code:
                      <root Ebenen="1011">
                      	<Gruppe id="R" Gruppenname="Gruppe1">
                      		<Gruppe id="R A" Gruppenname="Gruppe2">
                      			<Gruppe id="R AA"  Gruppenname="Gruppe3_1">
                      				<Produkt sortierfeld="x"/>
                      			</Gruppe>
                      			<Gruppe id="R AE" Gruppenname="Gruppe3_2">
                      				<Produkt sortierfeld="x">
                      					<Eigenschaften>
                      						<Titel sortierfeld="a">
                      							<Eigenschaften>
                      								<Referenz idref="R A" Name="Gruppe2"/>
                      							</Eigenschaften>
                      						</Titel>
                      						<Titel sortierfeld="b">
                      							<Eigenschaften>
                      								<Referenz idref="R AA" Name="Gruppe3_1"/>
                      							</Eigenschaften>
                      						</Titel>
                      						<Titel sortierfeld="c">
                      							<Eigenschaften>
                      								<Referenz idref="R AA1" Name="Gruppe4"/>
                      							</Eigenschaften>
                      						</Titel>
                      						<Titel sortierfeld="c">
                      							<Eigenschaften>
                      								<Referenz idref="R AA1" Name="Gruppe4"/>
                      							</Eigenschaften>
                      						</Titel>
                      						<Titel sortierfeld="a">
                      							<Eigenschaften>
                      								<Referenz idref="R AG" Name="Gruppe3_3"/>
                      							</Eigenschaften>
                      						</Titel>
                      					</Eigenschaften>
                      				</Produkt>		
                      			</Gruppe>
                      		</Gruppe>
                      	</Gruppe>
                      	<Gruppe id="S" Gruppenname="Gruppe1">
                      		<Gruppe id="S A" Gruppenname="Gruppe2">
                      			<Gruppe id="S A"  Gruppenname="Gruppe3_1">
                      				<Produkt sortierfeld="x"/>
                      			</Gruppe>
                      		</Gruppe>
                      	</Gruppe>	
                      </root>
                      XSL:
                      Code:
                      <xsl:stylesheet
                        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                        xmlns:xs="http://www.w3.org/2001/XMLSchema"
                        xmlns:mf="http://example.com/mf"
                        exclude-result-prefixes="xs mf"
                        version="2.0">
                      
                        <xsl:output indent="yes"/>
                        <xsl:strip-space elements="*"/>
                        
                        <xsl:function name="mf:distinct-and-sort" as="node()*">
                          <xsl:param name="groups" as="element(Gruppe)*"/>
                         <xsl:for-each-group select="$groups" group-by="@id">
                            <xsl:sort select="current-grouping-key()"/>
                            <Gruppe id="{current-grouping-key()}">
                      		<Gruppenname><xsl:value-of select="current-group()[1]/@Gruppenname"/></Gruppenname>
                      		<xsl:for-each select="current-group()/Produkt">
                      			<xsl:sort select="@sortierfeld"/>
                      			<xsl:copy-of select="."/>	
                      		</xsl:for-each>
                              <xsl:sequence select="mf:distinct-and-sort(current-group()/Gruppe)"/>
                            </Gruppe>
                          </xsl:for-each-group>
                        </xsl:function>
                      	
                        <xsl:key name="k1"
                          match="Titel"
                          use="for $length in (1 to string-length(Eigenschaften/Referenz/@idref))
                               return substring(Eigenschaften/Referenz/@idref, 1, $length)"/>
                               
                        <xsl:variable name="ebenen" select="string-length(root/@Ebenen)" as="xs:integer"/>     
                        
                        <xsl:template match="root">
                          <xsl:variable name="all-groups" as="element(Gruppe)*">
                            <xsl:apply-templates
                              select="Gruppe, descendant::Titel[not(substring(Eigenschaften/Referenz/@idref, 1, 1) = /Root/Gruppe/@id)]">
                                <xsl:with-param name="level" select="1"/>
                            </xsl:apply-templates> 
                          </xsl:variable>
                          <xsl:copy>
                            <xsl:sequence select="mf:distinct-and-sort($all-groups)"/>
                          </xsl:copy>
                        </xsl:template>
                      	
                        <xsl:template match="Gruppe">
                          <xsl:variable name="level" as="xs:integer" select="count(ancestor::Gruppe) + 3"/>
                      	<xsl:choose>
                      		<xsl:when test="parent::Gruppe/@id = @id">
                      			<xsl:apply-templates select="descendant::Produkt" mode="leaf"/>
                      		</xsl:when>
                      		<xsl:otherwise>
                      			<xsl:copy>
                      				  <xsl:copy-of select="@id | @Gruppenname"/>
                      					<xsl:apply-templates select="Produkt" mode="leaf"/>
                      				  <xsl:apply-templates
                      					select="Gruppe, key('k1', @idref, current())[string-length(Eigenschaften/Referenz/@idref) gt string-length(current()/@id)]">
                      					  <xsl:with-param name="level" select="$level"/>
                      				  </xsl:apply-templates>
                      				</xsl:copy>
                      		</xsl:otherwise>		
                      	</xsl:choose>   
                        </xsl:template>
                      	
                        <xsl:template match="Titel">
                          <xsl:param name="level" as="xs:integer"/>	
                          <Gruppe id="{substring(Eigenschaften/Referenz/@idref, 1, $level)}" Gruppenname="{Eigenschaften/Referenz/@Name}">
                              <xsl:variable name="next-level" as="xs:integer" select="if ($level eq 1) then 3 else $level + 1"/>
                              <xsl:choose>
                                <xsl:when test="(string-length(Eigenschaften/Referenz/@idref) ge $next-level) and $next-level le $ebenen">
                                  <xsl:apply-templates select=".">
                                    <xsl:with-param name="level" select="$next-level"/>
                                  </xsl:apply-templates>
                                </xsl:when>
                                <xsl:otherwise>
                                  <xsl:apply-templates select="." mode="leaf"/>
                                </xsl:otherwise>
                              </xsl:choose>
                            </Gruppe>
                        </xsl:template>
                      	
                        <xsl:template match="Produkt | Titel" mode="leaf">
                          <Produkt>
                            <xsl:copy-of select="@*"/>
                          </Produkt>
                        </xsl:template>
                      
                      </xsl:stylesheet>
                      Ausgabe-XML:
                      Code:
                      <root>
                      	<Gruppe id="R">
                      		<Gruppenname>Gruppe1</Gruppenname>
                      		<Gruppe id="R A">
                      			<Gruppenname>Gruppe2</Gruppenname>
                      			<Produkt sortierfeld="a"/>
                      			<Gruppe id="R AA">
                      				<Gruppenname>Gruppe3_1</Gruppenname>
                      				<Produkt sortierfeld="b"/>
                      				<Produkt sortierfeld="c"/>
                      				<Produkt sortierfeld="c"/>
                      				<Produkt sortierfeld="x"/>
                      			</Gruppe>
                      			<Gruppe id="R AE">
                      				<Gruppenname>Gruppe3_2</Gruppenname>
                      				<Produkt sortierfeld="x"/>
                      			</Gruppe>
                      			<Gruppe id="R AG">
                      				<Gruppenname>Gruppe3_3</Gruppenname>
                      				<Produkt sortierfeld="a"/>
                      			</Gruppe>
                      		</Gruppe>
                      	</Gruppe>
                      	<Gruppe id="S">
                      		<Gruppenname>Gruppe1</Gruppenname>
                      		<Gruppe id="S A">
                      			<Gruppenname>Gruppe2</Gruppenname>
                      			<Produkt sortierfeld="x"/>
                      		</Gruppe>
                      	</Gruppe>
                      </root>

                      Comment

                      Working...
                      X