Cassiopea: Mehrfach-Strukturierung eines flachen Dokuments

Hallo,

ich habe folgende Ausgangsstruktur:

  
<root>  
   <heading1>heading1</heading1>  
   <para>para 1</para>  
   <listelement first="yes">listelement 1</listelement>  
   <listelement>listelement 2</listelement>  
   <listelement last="yes">listelement 3</listelement>  
   <para>para 2</para>  
   <para>para 3</para>  
   <heading2>heading2-1</heading2>  
   <para>para 1-1</para>  
   <para>para 1-2</para>  
   <listelement_ol first="yes">listelement 1-1OL</listelement_ol>  
   <listelement_ol last="yes">listelement 1-2 OL</listelement_ol>  
   <para>para 1-3</para>  
   <para>para 1-4</para>  
   <heading2>heading2-2</heading2>  
   <para>para 2-1</para>  
   <listelement_ol first="yes">listelement 2-1 OL</listelement_ol>  
   <listelement_ol last="yes">listelement 2-2 OL</listelement_ol>  
   <para>para 2-2</para>  
    <listelement first="yes">listelement 2-1</listelement>  
    <listelement last="yes">listelement 2-2</listelement>  
    <para>para 1-1</para>  
    <para>para 1-2</para>  
</root  

Nun soll eine Strukturierung erfolgen.
1. Jede Datei hat nur ein Element heading1 - dass heißt die erste Strukturierung kann fix mittels LRE sect1 vorgenommen werden
2. Anschließend wird mittels xsl:for-each-group gruppiert und zwar nach allen heading2-Elementen
3. Weiterhin müssen alle Elemente listelement bzw. listelement_ol zu Listen gruppiert werden

Somit ergibt sich folgender Output:

  
<test>  
   <sect1>  
      <title>heading1</title>  
      <p>para 1</p>  
      <liste_ul>  
         <li>listelement 1</li>  
         <li>listelement 2</li>  
         <li>listelement 3</li>  
         <p>para 2</p>  
         <p>para 3</p>  
      </liste_ul>  
      <sect2>  
         <title2>heading2-1</title2>  
         <p>para 1-1</p>  
         <p>para 1-2</p>  
         <liste_ol>  
            <li>listelement 1-1OL</li>  
            <li>listelement 1-2 OL</li>  
         </liste_ol>  
         <p>para 1-3</p>  
         <p>para 1-4</p>  
      </sect2>  
      <sect2>  
         <title2>heading2-2</title2>  
         <p>para 2-1</p>  
         <liste_ol>  
            <li>listelement 2-1 OL</li>  
            <li>listelement 2-2 OL</li>  
         </liste_ol>  
         <p>para 2-2</p>  
         <li>listelement 2-1</li>  
         <li>listelement 2-2</li>  
         <p>para 1-1</p>  
         <p>para 1-2</p>  
      </sect2>  
   </sect1>  
</test>  

Die XSLT baut sich wiefolgt auf:

  
<xsl:template match="/">  
        <test>  
            <sect1>  
                <title>  
                    <xsl:value-of select="//heading1"/>  
                </title>  
                <xsl:call-template name="struktur"/>  
            </sect1>  
        </test>  
    </xsl:template>  
  
    <xsl:template name="struktur">  
        <xsl:for-each-group select="//para|//heading2|//listelement|//listelement_ol"  
            group-starting-with="heading2">  
            <xsl:choose>  
                <xsl:when test="current()[self::heading2]">  
                    <sect2>  
                        <xsl:call-template name="build_liste"/>  
                    </sect2>  
                </xsl:when>  
                <xsl:otherwise>  
                    <xsl:call-template name="build_liste"/>  
                </xsl:otherwise>  
            </xsl:choose>  
        </xsl:for-each-group>  
    </xsl:template>  
  
    <xsl:template name="build_liste">  
        <xsl:for-each-group select="current-group()" group-starting-with="listelement[@first]|listelement_ol[@first]">  
            <xsl:choose>  
                <xsl:when test="current()[self::listelement|self::listelement_ol]">  
                    <xsl:for-each-group select="current-group()" group-ending-with="listelement[@last]|listelement_ol[@last]">  
                        <xsl:choose>  
                            <xsl:when test="current()[self::listelement|self::listelement_ol]">  
                                <xsl:choose>  
                                    <xsl:when  test="current()[self::listelement]">  
                                        <liste_ul>  
                                            <xsl:apply-templates select="current-group()"/>  
                                        </liste_ul>  
                                    </xsl:when>  
                                    <xsl:when  test="current()[self::listelement_ol]">  
                                        <liste_ol>  
                                            <xsl:apply-templates select="current-group()"/>  
                                        </liste_ol>  
                                    </xsl:when>  
                                </xsl:choose>  
                            </xsl:when>  
                            <xsl:otherwise>  
                                <xsl:apply-templates select="current-group()"/>  
                            </xsl:otherwise>  
                        </xsl:choose>  
                    </xsl:for-each-group>  
                </xsl:when>  
                <xsl:otherwise>  
                    <xsl:apply-templates select="current-group()"/>  
                </xsl:otherwise>  
            </xsl:choose>  
        </xsl:for-each-group>  
    </xsl:template>  
  
  
    <xsl:template match="heading1">  
        <title>  
            <xsl:apply-templates/>  
        </title>  
    </xsl:template>  
  
    <xsl:template match="heading2">  
        <title2>  
            <xsl:apply-templates/>  
        </title2>  
    </xsl:template>  
  
    <xsl:template match="para">  
        <p>  
            <xsl:apply-templates/>  
        </p>  
    </xsl:template>  
  
    <xsl:template match="listelement">  
        <li>  
            <xsl:apply-templates/>  
        </li>  
    </xsl:template>  
  
    <xsl:template match="listelement_ol">  
        <li>  
            <xsl:apply-templates/>  
        </li>  
    </xsl:template>  

Innerhalb des Templates namens struktur wird zuerst eine Gruppierung nach sect2 gemacht - somit werden die Unterkapitel erzeugt.

Das Template build_liste wird nun genutzt, um Listen zu gruppieren bzw. zu strukturieren (da Listen auch als Kindelemente von heading1 vorkommen können, erfolgt auch an dieser Stelle ein Templateaufruf von build_liste).

Die erste Gruppierung funktioniert so, dass alle Listenelemente sowie die Knoten nach den Listenelementen gefiltert werden (d.h. alle Absätze vor den Listenelementen werden ausgeschlossen). Die zweite innere Gruppierung filtert nun alle Absätze para nach den Listen aus - so dass final nur die Listenelemente übrigbleiben, welche nun gruppiert werden (inklusive der Unterscheidung, ob eine geordnete oder ungeordnete Liste vorliegt).

Das ganze funktioniert soweit auch gut - da gibts nichts zu mekern. Meine Fragen sind daher:

1. Geht das nicht einfacher? Anstatt erst die Absätze vor und dann nach Listen auszuschließen - geht es nicht, dass man sagen kann: Alle Elemente namens listelement bzw. listelement_ol zusammenfassen/gruppieren? Leider hatte ich immer das Problem, dass ich die Knoten davor und danach (genauer die Absätze) mittels apply-templates oder current-group() nicht ausgeben konnte (Denn: es können ja auch mehrere Listen zwischen zwei heading2 Elementen vorkommen).

2. Ich habe für die Funktionen group-starting-with und group-ending-with jeweils händisch das Attribut @last und @first für listelement bzw. listelement_ol hinzugefügt - um die Gruppierung durchzuführen.
  Wie kann man nun den XPath formulieren ohne diese Attribute? Ich möchte ja immer das erste Listenelement auswählen, welches als DIREKTEN Vorgänger ein Absatz para hat bzw. für @last umgekehrt (jeweils der akutellen Gruppe). Aber die Formulierungen:
listelement[psoition()=1 and preceding-sibling::para] nützt hier irgendwie nicht viel.

Ich hoffe, jemand kann mir die Fragen bzw. eine der Fragen beantworten. Gerade das zweite Problem bringt mich ein wenig zur Verzweiflung.

Hoffentlich anwortet jemand - bitte *verzweifel*

Mfg Cassiopea

  1. Okay, ich habe gerade mal gemerkt, dass der Output nicht richtig war - da hatte ich wohl gerade an der XSLT gebastelt:

      
    <test>  
       <sect1>  
          <title>heading1</title>  
          <p>para 1</p>  
          <liste_ul>  
             <li>listelement 1</li>  
             <li>listelement 2</li>  
             <li>listelement 3</li>  
          </liste_ul>  
          <p>para 2</p>  
          <p>para 3</p>  
          <sect2>  
             <title2>heading2-1</title2>  
             <p>para 1-1</p>  
             <p>para 1-2</p>  
             <liste_ol>  
                <li>listelement 1-1OL</li>  
                <li>listelement 1-2 OL</li>  
             </liste_ol>  
             <p>para 1-3</p>  
             <p>para 1-4</p>  
          </sect2>  
          <sect2>  
             <title2>heading2-2</title2>  
             <p>para 2-1</p>  
             <liste_ol>  
                <li>listelement 2-1 OL</li>  
                <li>listelement 2-2 OL</li>  
             </liste_ol>  
             <p>para 2-2</p>  
             <liste_ul>  
                <li>listelement 2-1</li>  
                <li>listelement 2-2</li>  
             </liste_ul>  
             <p>para 1-1</p>  
             <p>para 1-2</p>  
          </sect2>  
       </sect1>  
    </test>  
    
    

    So ist es richtig ;)

    Nun hoffe ich mal auf antworten.

    1. Da kann ich mich bei diesem Forum doch für die zahlreichen Antworten nur bedanken!!!

      Dabei hab ich meine Lösung schon präsentiert und wollte lediglich ein paar Hilfestellungen bzw. Antworten, wie dieses Problem mit der Selektion von dem ersten bzw. letzten Knoten des Elements listelement jeder aktuellen Gruppe anzugehen ist.

      Naja. DANKE!!!

      1. Hallo,

        Da kann ich mich bei diesem Forum doch für die zahlreichen Antworten nur bedanken!!!

        Du weißt schon, dass hier niemand das Recht hat eine Antwort zu fordern und dass ebenso niemand die Garantie hat eine zu bekommen.

        Dabei hab ich meine Lösung schon präsentiert und wollte lediglich ein paar Hilfestellungen bzw. Antworten, wie dieses Problem mit der Selektion von dem ersten bzw. letzten Knoten des Elements listelement jeder aktuellen Gruppe anzugehen ist.

        Stimmt, das hast du. Ich habe mir auch deine Dateien geholt, dennoch bin ich z.B. bisher schlicht nicht dazugekommen, mir die Sache ganz genau anzusehen. Deine Frage ist nicht zwischen Tür und Angel zu beantworten. Die Antwort muss man sich auch erst wirklich erarbeiten und überlegen, welche Möglichkeiten deine Anforderungen am besten erfüllen.

        Grüße
        Thomas

        1. Danke Thomas,

          das wenigstens mal jemand reagiert hat.

          Es ärgert mich einfach nur, weil ich schon soweit gekommen bin und nun eben an diesem letzten Schritt hänge!

          Es wundert mich einfach nur - gibt es den keine Möglichkeit mittels xsl:for-each-group oder ähnlichem Knoten der gleichen Bezeichnung, die aufeinanderfolgen (direkt) zu gruppieren? Das ist ja genau das was ich suche. Mir kommt meine Lösung eben so umständlich vor.

          Gruß Cassiopea

            1. Thomas,

              ich dank dir Tausend Mal *freudentränen*

              ich hab an dem Problem so lange gesehen, total verzweifelt - bis ich überhaupt erstmal meine lösung zu recht gebastelt hatte.

              und dein beitrag war das letzte puzzelstück was mir noch gefehlt hatte.

              ich hab aber auch im internet geguckt - nichts gefunden - in büchern -> es waren immer bloß einfache strukturierungen/gruppierungen, die nicht weitergeholfen haben.

              DANKE für die Hilfe & das Feedback

              MFG Cassiopea

  2. Hallo,

    Meine Fragen sind daher:

    1. Geht das nicht einfacher? Anstatt erst die Absätze vor und dann nach Listen auszuschließen - geht es nicht, dass man sagen kann: Alle Elemente namens listelement bzw. listelement_ol zusammenfassen/gruppieren? Leider hatte ich immer das Problem, dass ich die Knoten davor und danach (genauer die Absätze) mittels apply-templates oder current-group() nicht ausgeben konnte (Denn: es können ja auch mehrere Listen zwischen zwei heading2 Elementen vorkommen).

    2. Ich habe für die Funktionen group-starting-with und group-ending-with jeweils händisch das Attribut @last und @first für listelement bzw. listelement_ol hinzugefügt - um die Gruppierung durchzuführen.
        Wie kann man nun den XPath formulieren ohne diese Attribute? Ich möchte ja immer das erste Listenelement auswählen, welches als DIREKTEN Vorgänger ein Absatz para hat bzw. für @last umgekehrt (jeweils der akutellen Gruppe). Aber die Formulierungen:
      listelement[psoition()=1 and preceding-sibling::para] nützt hier irgendwie nicht viel.

    Ich hoffe, jemand kann mir die Fragen bzw. eine der Fragen beantworten. Gerade das zweite Problem bringt mich ein wenig zur Verzweiflung.

    Im Wesentlichen gibt es nur zwei kleinere Vereinfachungsmöglichkeiten:

    <xsl:template name="struktur">  
    		<xsl:for-each-group select="root/heading1/following-sibling::*" group-starting-with="heading2"
    ~~~>  
      
      
    2) um @last und @first weglassen zu können:  
      
    ~~~xml
    		<xsl:for-each-group select="current-group()" group-starting-with="listelement[preceding-sibling::*[1][not(local-name() = 'listelement')]] | listelement_ol[preceding-sibling::*[1][not(local-name() = 'listelement_ol')]]">  
    			<xsl:choose>  
    				<xsl:when test="current()[self::listelement|self::listelement_ol]">  
    					<xsl:for-each-group select="current-group()" group-ending-with="listelement[following-sibling::*[1][not(local-name() = 'listelement')]] | listelement_ol[following-sibling::*[1][not(local-name() = 'listelement_ol')]]">  
    						<xsl:choose>
    

    Grüße
    Thomas