Announcement

Collapse
No announcement yet.

Problem bei xsl:for-each-group und Auswahl zwischen ul und ol

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

  • Problem bei xsl:for-each-group und Auswahl zwischen ul und ol

    Hallo Leute,

    ich möchte ein XML-Dokument analysieren, die Inhalte darin gruppieren und in HTML-Listen, sowohl ordered (ol) wie auch unordered (ul) ausgeben. Solange ich mich explizit für eine Art entscheide, dann ist das kein Problem und es kommt eine ansehnliche Ausgabe raus.

    Wenn ich aber über eine xsl:choose-Anweisung eine Auswahl vornehmen möchte, ob die Liste nun UL oder OL ist, dann kommt irgendwie immer Quark raus. Vielleicht habt Ihr ja Ideen dazu.

    Zunächst aber erst mal die XML-Datei (beispielhaft):

    Code:
    <dokument>
            <EinzugListe>
    		<Einzug aufz="1." level="0">_1_</Einzug>
    		<Einzug aufz="2." level="0">_2_</Einzug>
    		<Einzug aufz="3." level="0">_3_</Einzug>
    		<Einzug aufz="4." level="0">_4_</Einzug>
    		<Einzug aufz="-" level="1">_4_1</Einzug>
    		<Einzug aufz="-" level="1">_4_2</Einzug>
    		<Einzug aufz="5." level="0">_5_</Einzug>
    		<Einzug aufz="-" level="1">_5_1</Einzug>
    		<Einzug aufz="-" level="1">_5_2</Einzug>
    		<Einzug aufz="-" level="1">_5_3</Einzug>
    		<Einzug aufz="6." level="0">_6_</Einzug>
    		<Einzug aufz="7." level="0">_7_</Einzug>
    		<Einzug aufz="-" level="1">_7_1</Einzug>
    		<Einzug aufz="-" level="1">_7_2</Einzug>
    		<Einzug aufz="-" level="1">_7_3</Einzug>
    		<Einzug aufz="-" level="1">_7_4</Einzug>
    		<Einzug aufz="8." level="0">_8_</Einzug>
    		<Einzug aufz="-" level="1">_8_1</Einzug>
    		<Einzug aufz="-" level="1">_8_2</Einzug>
    		<Einzug aufz="-" level="1">_8_3</Einzug>
    		<Einzug aufz="-" level="1">_8_4</Einzug>
    		<Einzug aufz="9." level="0">_9_</Einzug>
    		<Einzug aufz="-" level="1">_9_1</Einzug>
    		<Einzug aufz="-" level="1">_9_2</Einzug>
    		<Einzug aufz="-" level="2">_9_2_1</Einzug>
    		<Einzug aufz="-" level="2">_9_2_2</Einzug>
    		<Einzug aufz="-" level="2">_9_2_3</Einzug>
    		<Einzug aufz="-" level="2">_9_2_4</Einzug>
    		<Einzug aufz="-" level="2">_9_2_5</Einzug>
    		<Einzug aufz="-" level="1">_9_3</Einzug>
    		<Einzug aufz="-" level="1">_9_4</Einzug>
    		<Einzug aufz="10." level="0">_10_</Einzug>
    		<Einzug aufz="-" level="1">_10_1</Einzug>
    		<Einzug aufz="-" level="1">_10_2</Einzug>
    		<Einzug aufz="11." level="0">_11_</Einzug>
    		<Einzug aufz="12." level="0">_12_</Einzug>
    		<Einzug aufz="-" level="1">_12_1</Einzug>
    		<Einzug aufz="-" level="1">_12_2</Einzug>
    		<Einzug aufz="-" level="1">_12_3</Einzug>
    		<Einzug aufz="-" level="1">_12_4</Einzug>
    		<Einzug aufz="13." level="0">_13_</Einzug>
    		<Einzug aufz="-" level="1">_13_1</Einzug>
    		<Einzug aufz="-" level="2">_13_1_1</Einzug>
    		<Einzug aufz="-" level="2">_13_1_2</Einzug>
    		<Einzug aufz="-" level="2">_13_1_3</Einzug>
    		<Einzug aufz="-" level="2">_13_1_4</Einzug>
    		<Einzug aufz="-" level="2">_13_1_5</Einzug>
    		<Einzug aufz="14." level="0">_14_</Einzug>
    	</EinzugListe>
    </dokument>
    Nun versuche ich diese Datei mit folgender XSL-Transformation zu transformieren:

    Code:
    <xsl:template match="EinzugListe">
    	<ul>
    		<xsl:call-template name="ebene">
    			<xsl:with-param name="liste" select="Einzug" />
    			<xsl:with-param name="level" select="0" />
    		</xsl:call-template>
    	</ul>
    </xsl:template>
    
    <xsl:template name="ebene">
    	<xsl:param name="liste" required="yes" as="element()*" />
    	<xsl:param name="level" required="yes" as="xs:integer" />
    	
    	<xsl:for-each-group select="$liste" group-starting-with="*[xs:integer(@level) eq $level]">
    		<li><xsl:value-of select="." />
    			<ul>
    				<xsl:call-template name="ebene">
    					<xsl:with-param name="liste" select="current-group() except ."/>
    					<xsl:with-param name="level" select="$level + 1"/>
    				</xsl:call-template>
    			</ul>
    		</li>
    	</xsl:for-each-group>
    </xsl:template>
    Ausgabe dieser Transformation ist dann so etwas wie:

    HTML Code:
    <ul>
      <li>_1_</li>
      <li>_2_</li>
      <li>_3_</li>
      <li>_4_
      <ul>
        <li>_4_1</li>
        <li>_4_2</li>
      </ul>
      </li>
      <li>_5_
      <ul>
        <li>_5_1</li>
        <li>_5_2</li>
        <li>_5_3</li>
      </ul>
      </li>
      <li>_6_</li>
      <li>_7_
      <ul>
        <li>_7_1</li>
        <li>_7_2</li>
        <li>_7_3</li>
        <li>_7_4</li>
      </ul>
      </li>
      <li>_8_
      <ul>
        <li>_8_1</li>
        <li>_8_2</li>
        <li>_8_3</li>
        <li>_8_4</li>
      </ul>
      </li>
      <li>_9_
      <ul>
        <li>_9_1</li>
        <li>_9_2
        <ul>
          <li>_9_2_1</li>
          <li>_9_2_2</li>
          <li>_9_2_3</li>
          <li>_9_2_4</li>
          <li>_9_2_5</li>
        </ul>
        </li>
        <li>_9_3</li>
        <li>_9_4</li>
      </ul>
      </li>
      <li>_10_
      <ul>
        <li>_10_1</li>
        <li>_10_2</li>
      </ul>
      </li>
      <li>_11_</li>
      <li>_12_
      <ul>
        <li>_12_1</li>
        <li>_12_2</li>
        <li>_12_3</li>
        <li>_12_4</li>
      </ul>
      </li>
      <li>_13_
      <ul>
        <li>_13_1
        <ul>
          <li>_13_1_1</li>
          <li>_13_1_2</li>
          <li>_13_1_3</li>
          <li>_13_1_4</li>
          <li>_13_1_5</li>
        </ul>
        </li>
      </ul>
      </li>
      <li>_14_</li>
    </ul>
    Das mit der 14 unten klappt zwar auch nicht so reibungslos, aber da könnte ich evtl. noch mit leben (es sei denn, Ihr habt noch ne Idee).

    Übrigens: Mein XMLSpy meckert ständig rum, dass xsl:for-each-group nicht in xsl:template stehen darf (wenn ich der XML-Datei das XSL direkt zuweise). Hier evtl. jemand ne Idee?

    Wenn ich die Transformation nun so umsetze, gehen aber Informationen verloren, nämlich die Nummerierungen der einzelnen Unterpunkte. Und genau das möchte ich verhindern, indem ich bei allen Elementen, die ein Attribut "aufz" mit dem Wert "-" haben als UL dargestellt werden und alle anderen als OL. Habe hier auch schon einige Versuche mit xsl: choose und xsl: otherwise hinter mir, bekomme es aber nicht hin, dass es so raus kommt, wie es sein soll.

    Hier mal mein Versuch für ein anderes XSL:

    Code:
    <xsl:template match="EinzugListe">
    	<xsl:call-template name="ebene">
    		<xsl:with-param name="liste" select="Einzug" />
    		<xsl:with-param name="level" select="0" />
    	</xsl:call-template>
    </xsl:template>
    
    <xsl:template name="ebene">
    	<xsl:param name="liste" required="yes" as="element()*" />
    	<xsl:param name="level" required="yes" as="xs:integer" />
    	
    	<xsl:choose>
    		<xsl:when test="./@aufz='-'">
    				<ul>
    					<xsl:for-each-group select="$liste" group-starting-with="*[xs:integer(@level) eq $level]">
    						<li><xsl:value-of select="." />
    						<xsl:call-template name="ebene">
    							<xsl:with-param name="liste" select="current-group() except ."/>
    							<xsl:with-param name="level" select="$level + 1"/>
    						</xsl:call-template>
    						</li>
    					</xsl:for-each-group>
    				</ul>
    		</xsl:when>
    		<xsl:otherwise>
    				<ol>
    					<xsl:for-each-group select="$liste" group-starting-with="*[xs:integer(@level) eq $level]">
    						<li><xsl:value-of select="." />
    							<xsl:call-template name="ebene">
    								<xsl:with-param name="liste" select="current-group() except ."/>
    								<xsl:with-param name="level" select="$level + 1"/>
    							</xsl:call-template>
    						</li>
    					</xsl:for-each-group>
    				</ol>
    		</xsl:otherwise>
    	</xsl:choose>
    </xsl:template>
    Da kommt ja was ansehnliches raus, aber richtig sauberer HTML-Code sieht m.E. dann auch anders aus ;-)

    Über Lösungsvorschläge wäre ich sehr dankbar, ich habe auch die Möglichkeit das XML ein wenig umzustricken (vielleicht ne andere Verschachtelung anstatt des "level"-Attributs o.ä.), also kann die Lösung auch in diese Richtung erfolgen.

    Vielen Dank für Eure Antworten und Eure Hilfe.

    Viele Grüße
    cws

  • #2
    Da die Option offenbar besteht: Verschachtele besser die Einzug-Elemente in der gewünschten Weise untereinander, dann ergibt sich die Listenlogik von selbst. Zum Problem mit dem xsl:for-each-group vermute ich, dass eine Interpretation als XSLT 1.0 erfolgt und darin gibt es dieses Element noch nicht (interner Prozessor = MSXML vom IE?).

    Comment


    • #3
      Hallo Thomas,

      vielen Dank erst mal für Deine schnelle Antwort.

      Mach mal einen Vorschlag für eine neue Verschachtelung der Elemente. Ich könnte es mir jetzt so vorstellen (beispielhaft ohne Attribute):

      HTML Code:
      <EinzugListe>
        <Einzug>_1_</Einzug>
        <Einzug>_2_
          <EinzugListe>
            <Einzug>_2_1</Einzug>
            <Einzug>_2_2
              <EinzugListe>
                <Einzug>_2_2_1</Einzug>
              </EinzugListe>
            </Einzug>
            <Einzug>_2_3</Einzug>
          </EinzugListe>
        </Einzug>
        <Einzug>_3_</Einzug>
        <Einzug>_4_
          <EinzugListe>
            <Einzug>_4_1</Einzug>
            <Einzug>_4_2
              <EinzugListe>
                <Einzug>_4_2_1</Einzug>
              </EinzugListe>
            </Einzug>
          </EinzugListe>
        </Einzug>
        <Einzug>_5_</Einzug>
      </EinzugListe>
      Ist es das was Du meinst oder hast Du noch einen anderen Vorschlag? Problem ist nur, dass ich das Attribut "aufz" wahrscheinlich nicht dem Element "EinzugListe" übertragen kann, sondern dass das bei "Einzug" bleiben muss. Wie mache ich dann die gewünschte Unterscheidung zwischen UL und OL und wie werte ich dann die Liste neu aus. Über kleine Tipps wäre ich da sehr dankbar.


      Nochmal zu dem XMLSpy-Parsing-Problem:
      Bei mir ist eingestellt "Integrierter XSLT-Prozessor", nicht der MSXML Parser und auch kein externes Programm. Soll ich mir lieber den Saxon herunterladen und den integrieren? Kann ich irgendwo nachprüfen welche Version der interne XSLT-Prozessor hat? Habe XMLSpy 2008.

      Außerdem ergibt sich jetzt noch ein anderer Fehler und die Ausgabe meint "xsltherwise" darf kein "xsl:for-each-group" enthalten.

      Nochmal zum Verständnis, die Ausgabe kommt wenn ich meinem XML-Dokument das XSL in XMLSpy zuweise und dann bei der XML-Datei auf die Browser-Ansicht gehe. Es steht dann also so was wie
      Code:
      <?xml-stylesheet type="text/xsl" href="...Pfad-zur-XSL-Datei..."?>
      im Kopf des XML-Dokuments.

      Im XSL-Dokument habe ich Folgendes im Kopf stehen (vielleicht ist da ja was falsch):
      HTML Code:
      <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/1999/xhtml">
      <xsl:output method="xhtml" />
      Nochmals vielen Dank für Deine Hilfe.

      Viele Grüße
      cws

      Comment


      • #4
        Die Struktur ist jetzt brauchbarer. An welchen Stellen sollen nochmal ol- bzw. ul-Elemente kommen?

        Das Problem mit der Transformation ist klar: Wenn ein XML-Dokument die Zuweisung über <?xml-stylesheet ... ?> enthält und dann die Vorschau im Browser gewählt wird, kommt auch dieser zum Einsatz (also unter XMLSpy der IE auf XSLT 1.0-Basis).

        Wähle alternativ den Menüpunkt "XSL-Transformation" oder gleich die F10-Taste, dann wird der interne bzw. externe Prozessor verwendet. Du hast beim externen Prozessor für XSLT/XPath 2.0 die Wahl zwischen Saxon 8.9 o. 9.0 und AltovaXML 2007 o. 2008. Binde diese so ein (Pfade entsprechend anpassen):

        X:\AltovaXML2008\AltovaXML.exe -xslt2 %3 -in %1 -out %2

        java -jar X:\Saxon9\Java\saxon9.jar -o %2 %1 %3

        Comment


        • #5
          Hier noch eine Idee für die neue Struktur:

          Code:
          <xsl:template match="@* | node()">
            <xsl:copy>
              <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
          </xsl:template>
          
          <xsl:template match="EinzugListe">
            <ol>
              <xsl:apply-templates/>
            </ol>
          </xsl:template>
          
          <xsl:template match="EinzugListe[count(ancestor::EinzugListe) > 1]">
            <ul>
              <xsl:apply-templates/>
            </ul>
          </xsl:template>
          
          <xsl:template match="Einzug">
            <li>
              <xsl:apply-templates/>
            </li>
          </xsl:template>
          
          <xsl:template match="text()">
            <!-- Zeilenumbrüche/Leerzeilen in li mit Text vermeiden -->
            <xsl:value-of select="normalize-space(.)"/>
          </xsl:template>
          HTML-Ergebnis:

          Code:
          <ol>
            <li>_1_</li>
            <li>_2_<ol>
                <li>_2_1</li>
                <li>_2_2<ul>
                    <li>_2_2_1</li>
                  </ul>
                </li>
                <li>_2_3</li>
              </ol>
            </li>
            <li>_3_</li>
            <li>_4_<ol>
                <li>_4_1</li>
                <li>_4_2<ul>
                    <li>_4_2_1</li>
                  </ul>
                </li>
              </ol>
            </li>
            <li>_5_</li>
          </ol>

          Comment

          Working...
          X