Announcement

Collapse
No announcement yet.

Durchlaufen und gruppieren einer Struktur

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

  • Durchlaufen und gruppieren einer Struktur

    Guten Tag liebes Forum,

    ich möchte gern mein Problem schildern:
    Ich habe eine XML-Struktur, die ich durchlaufen und ausgeben muss. Die sieht so hier aus:

    Code:
    <Attribute>
     <ErstesAttribut>
       <Label>Beschreibung 1</Label>
       <Value>Wert 1</Value>
       <Header>h1</Header>
     </ErstesAttribut>
     <ZweitesAttribut>
       <Label>Beschreibung 2</Label>
       <Value>Wert 2</Value>
       <Header>h1</Header>
     </ZweitesAttribut>
     <IrgendeinAttribut>
       <Label>Beschreibung N</Label>
       <Value>Wert N</Value>
       <Header>h2</Header>
     </IrgendeinAttribut>
    </Attribute>
    Die Knotennamen innerhalb von <Attribute> sind mir nicht bekannt, bzw. sind soviele, dass man auf die nicht einzeln zugreifen kann. Außerdem sollen die Attribute auf dem Bildschirm nach dem <Header> in jedem <Attribute> gruppiert werden. Ich weiß, die XML-Struktur ist nicht wirklich gut aufgebaut, aber sie ist nicht änderbar, da als Input von einem anderen System.
    Ich würde jetzt gern die Struktur durchlaufen, sie nach <Header> sortieren/gruppieren und mir dann zu jedem <Attribute> das Label und den Value ausgeben lassen, sodass die dann gruppiert nach dem Header untereinander erscheinen.
    Mein Problem nun ist, dass ich schonmal gar nicht weiß, wie ich auf die unbekannten Knoten-Name zugreifen kann, und somit wüßte ich auch nicht, wie ich die <xsl:sort...> Funktion richtig füttere (das wäre nämlich mein Ansatz gewesen)

    Viele Grüße,
    Christian

  • #2
    /Attribute/* selektiert alle Kindelemente des Wurzelelement "Attribute", das kannst du also benutzen, um auf die Elemente mit unbekannten bzw. verschiedenen Namen zuzugreifen. Mit XSLT 2.0 kann man zum Gruppieren dann for-each-group benutzen:
    Code:
    <xsl:template match="Attribute">
      <dl>
        <xsl:for-each-group select="*" group-by="Header">
          <dt><xsl:value-of select="current-grouping-key()"/></dt>
          <dd>
             <ul>
               <xsl:for-each select="current-group()">
                  <li><xsl:value-of select="concat(Label, ': ', Value)"/></li>
               </xsl:for-each>
             </ul>
          </dd>
       </xsl:for-each-group>
      </dl>
    </xsl:template>
    XSLT 2.0 kann man mit XML Spy oder Oxygen oder Stylus Studio bzw. programmatisch mit Saxon 9 (http://saxon.sourceforge.net/) oder AltovaXML (http://www.altova.com/altovaxml.html) oder XQSharp (http://www.xqsharp.com/) benutzen.

    Comment


    • #3
      Dankeschön für die schnelle Antwort :-)
      Leider kann ich aber nur XSLT 1.0 verwenden.

      Viele Grüße,
      Christian

      Comment


      • #4
        "*" als "wildcard" für Elemente gibt es auch in XSLT bzw. in XPath 1.0. Gruppieren muss man per "Muenchian grouping", siehe dazu http://www.jenitennison.com/xslt/grouping/muenchian.xml

        Comment


        • #5
          Hallo,

          danke. Ich habe nicht ganz verstanden, wie die erzeugten IDs bzw. die ausgelesenen Header auf die Struktur angewendet werden?

          Weil folgendes gibt nix aus:
          Code:
          <xsl:call-template name="DisplayGroupedAttributes"/>
          und dann:
          Code:
          	<xsl:template name="DisplayGroupedAttributes">
          		<fo:block>	
          			<xsl:key name="headerGrouped" match="Attributes/*" use="Header" />
          			   	<fo:table table-layout="fixed" width="100%" border="0.5pt solid red">
          					<fo:table-column column-width="50.0%"/>
          					<fo:table-column column-width="50.0%"/>
          					<fo:table-body>
          						<xsl:for-each select="contact[generate-id() = generate-id(key('headerGrouped', Header)[1])]">
          						<xsl:sort select="Header" />
          							<xsl:for-each select="key('headerGrouped', Header)">
          								<fo:table-row>
          									<fo:table-cell>
          										<fo:block>
          											<xsl:value-of select="Label"/>								
          										</fo:block>
          									</fo:table-cell>
          									<fo:table-cell>
          										<fo:block>
          										<xsl:value-of select="Value"/>
          										</fo:block>
          									</fo:table-cell>
          								</fo:table-row>
          							</xsl:for-each>			
          						</xsl:for-each>
          					</fo:table-body>
          				</fo:table>
          		</fo:block>			
          	</xsl:template>
          Es läuft ja über alle Header, aber wo müssen die Daten dann wiederum aus der Struktur geholt werden?

          Comment


          • #6
            Das xsl: key Element muss als Kind des xsl: stylesheet Element definiert werden, nicht in einem Template.
            Ansonsten ist das Wurzelelement im geposteten Beispiel "Attribute" und nicht "Attributes", da macht 'match="Attributes/*"' keinen Sinn. Auch 'xsl:for-each select="contact' macht mit dem geposteten Beispiel keinen Sinn, da gibt es gar keine "contact" Elemente.

            Mein XSLT 2.0 Beispiel nach XSLT 1.0 "übersetzt" sieht etwa so aus:
            Code:
            <xsl:stylesheet
              xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
              version="1.0">
              
              <xsl:output method="html" indent="yes"/>
              
              <xsl:key name="headerGrouped" match="Attribute/*" use="Header" />
            
              <xsl:template match="Attribute">
                <dl>
                  <xsl:for-each select="*[generate-id() = generate-id(key('headerGrouped', Header)[1])]">
                    <dt><xsl:value-of select="Header"/></dt>
                    <dd>
                       <ul>
                         <xsl:for-each select="key('headerGrouped', Header)">
                            <li><xsl:value-of select="concat(Label, ': ', Value)"/></li>
                         </xsl:for-each>
                       </ul>
                    </dd>
                 </xsl:for-each>
                </dl>
              </xsl:template>
              
            </xsl:stylesheet>
            Damit wird aus deinem Eingabebeispiel dann etwa
            Code:
            <dl>
               <dt>h1</dt>
               <dd>
                  <ul>
                     <li>Beschreibung 1: Wert 1</li>
                     <li>Beschreibung 2: Wert 2</li>
                  </ul>
               </dd>
               <dt>h2</dt>
               <dd>
                  <ul>
                     <li>Beschreibung N: Wert N</li>
                  </ul>
               </dd>
            </dl>
            Gut, du willst XSL-FO erzeugen, nicht HTML, aber eventuell reicht dir das Beispiel, um dein Stylesheet anzupassen. Wenn nicht, dann musst du mehr Kontext posten, damit man reproduzieren kann, warum es keine Ausgabe gibt. Nur ein '<xsl:call-template name="DisplayGroupedAttributes"/>' und das Template sagt mir gar nichts, solange ich nicht erkennen kann, was der Kontextknoten ist.

            Comment


            • #7
              Hallo,

              vielen Dank, mit dem Hinweis, dass der KEY direkt unter das Stylesheet muss, habe ich es hinbekommen, auch mit XSL-FO.

              Eine Frage habe ich daraufhin noch: Kann man sich aus dem KEY alle Elemente holen, die einen bestimmten Header haben (bspw. ist der Headername "Basics")? Nun möchte ich alle Unterelemente die nur diesem Header zugehören an einer anderen Stelle des Stylesheets anzeigen.

              Viele Grüße,
              C. Fleischmann

              Comment


              • #8
                Code:
                <xsl:apply-templates select="key('headerGrouped', 'Basics')" mode="mode1"/>
                und dann entsprechende Templates für "mode1", die die gewünschte Ausgabe erzeugen, sollten reichen.

                Comment


                • #9
                  Vielen Dank,

                  könntest du mir noch einen Hinweis geben, wie ich dann das Template mit dem mode="mode1" aufbauen muss, damit er die Daten in einer Schleife auch alle durchläuft? Das kenne ich bisher nicht.

                  Viele Grüße,
                  C.Fleischmann

                  Comment


                  • #10
                    Du kannst ein
                    Code:
                    <xsl:template match="Attribute/*" mode="mode1">
                    
                    </xsl:template>
                    schreiben, als Inhalt des Templates packst du dann die XSL-FO-Elemente, die für jedes Element ausgegeben werden sollen, also (nur als Beispiel)
                    Code:
                    <xsl:template match="Attribute/*" mode="mode1">
                      <fo:block>
                         <xsl:value-of select="Label"/>
                         <xsl:text>: </xsl:text>
                         <xsl:value-of select="Value"/>
                      </fo:block>
                    </xsl:template>
                    Ein Template mit explizitem "mode" funktioniert auch nicht anders als ein Template ohne "mode", es geht nur darum, per unterschiedlicher "modes" Knoten mehrfach verarbeiten zu können.

                    Comment


                    • #11
                      Hallo Martin,

                      irgendwie stehe ich immer noch auf dem Schlauch. Mit dem "mode" kann ich einen Knoten mehrfach verarbeiten? Das wäre in meinem Fall, dass jetzt alle gruppierten Elemente mit dem Header "Basics" automatisch duchlaufen und angezeigt werden? D.h. ich benötige in dem Template keine weitere Schleife, die mir dann nochmal alle Unterelemente von "Basics" durchgeht?

                      Demnach müsste folgendes funktionieren (mit meinem Beispiel XML vom Anfang):

                      Code:
                      <xsl:apply-templates select="key('headerGrouped', 'h1')" mode="mode1"/>
                      und dann das Template:

                      Code:
                      <xsl:template match="Attribute/*" mode="mode1">
                        <fo:block>
                           <xsl:value-of select="Label"/>
                           <xsl:text>: </xsl:text>
                           <xsl:value-of select="Value"/>
                        </fo:block>
                      </xsl:template>
                      sollte dann als Ausgabe haben:
                      Beschreibung 1:Wert 1
                      Beschreibung 2:Wert 2


                      Ich frag nur, ob ich das richtig verstanden habe, denn ich habe es so versucht und es kommt überhaupt keine Ausgabe. Den xsl:key habe ich natürlich noch drinnen gelassen...

                      Comment


                      • #12
                        Wo steht denn
                        Code:
                        <xsl:apply-templates select="key('headerGrouped', 'h1')" mode="mode1"/>
                        ? Das muss in einem Template stehen, das ausgeführt wird.

                        Comment


                        • #13
                          http://entwickler-forum.de/showthread.php?t=66761 hat ein Beispiel, wann/wie die Benutzung von modes sinnvoll ist.

                          Comment


                          • #14
                            Originally posted by Martin Honnen View Post
                            Wo steht denn
                            Code:
                            <xsl:apply-templates select="key('headerGrouped', 'h1')" mode="mode1"/>
                            ? Das muss in einem Template stehen, das ausgeführt wird.
                            Achso, ich dachte, ich muss damit das Template aufrufen. Jetzt verstehe ich aber noch nicht, wie ich darüber dann ein Template aufrufe, was mir nur die besagten Werte aus einer Gruppe ausgibt.

                            D.h. der Aufruf wäre:

                            Code:
                            <xsl:apply-templates select="Attribute" mode="mode1"/>
                            welcher dann das hier ausführt (nach deiner Aussage, dass das in einem Template stehen muss, welches ausgeführt wird):

                            Code:
                            <xsl:template match="Attribute" mode="mode1">
                             <xsl:apply-templates select="key('headerGrouped', 'h1')"/>
                            </xsl:template>
                            Nun weiß ich aber an dieser Stelle nicht weiter, wie muss der Name des Templates lauten, welches mit dem Key "applied" wird?

                            Comment


                            • #15
                              Knoten werden mit apply-templates zur Verarbeitung ausgewählt, der XSLT-Prozessor sucht dann passende Templates anhand des match-Attributes von Templates. Einen Namen braucht ein Template dafür gar nicht.
                              "key('headerGrouped', 'h1')" liefert eine Knotenmenge von Kindelemente von "Attribut"-Elementen, dazu muss man dann ein passendes Template schreiben, also etwa
                              Code:
                                <xsl:template match="Attribute/*" mode="mode1">...</xsl:template>

                              Comment

                              Working...
                              X