Search code examples
xsltgroupingnodes

Wrap groups of nodes without attribute in XSLT


I want to wrap groups of elements with a new parent element. Such groups appear in many places in the document. The elements have no attribute. With for-each-group I manage to include all desired elements of the document in a large group with a new parent element, but I want to distinguish the groups according to their occurrence. Found many similar questions and answers, but could not solve the problem.

My document

<modul>
    <body>
        <Standard>some text</Standard>
        <Herausforderung>some text</Herausforderung>
        <Standard>some text
           <d2tb>some text</d2tb>
        </Standard>
        <Aufzhlungbullets>some text</Aufzhlungbullets>
        <Aufzhlungbullets>some text</Aufzhlungbullets>
        <Aufzhlungbullets>some text</Aufzhlungbullets>
        <Standard>some text
           <d2ti>some text</d2ti>
        </Standard0>
        <Aufzhlungbullets>some text</Aufzhlungbullets>
        <Aufzhlungbullets>some text</Aufzhlungbullets>
        <Aufzhlungbullets>some text</Aufzhlungbullets>
        <Standard>some text</Standard>
   </body>
</modul>

Desired output

<modul>
        <body>
            <Standard>some text</Standard>
            <Herausforderung>some text</Herausforderung>
            <Standard>some text
               <d2tb>some text</d2tb>
            </Standard>
            <list>
               <Aufzhlungbullets>some text</Aufzhlungbullets>
               <Aufzhlungbullets>some text</Aufzhlungbullets>
               <Aufzhlungbullets>some text</Aufzhlungbullets>
            </list>
            <Standard>some text
               <d2ti>some text</d2ti>
            </Standard0>
            <list>
               <Aufzhlungbullets>some text</Aufzhlungbullets>
               <Aufzhlungbullets>some text</Aufzhlungbullets>
               <Aufzhlungbullets>some text</Aufzhlungbullets>
            </list>
            <Standard>some text</Standard>
       </body>
    </modul>

My XSLT-stylesheet

<xsl:template match="body">
        <list>
        <xsl:for-each-group select="Aufzhlungbullets" group-by=".">
                <xsl:copy-of select="current-group()"/>
        </xsl:for-each-group>
        </list>
    </xsl:template>

note: I know that this template suppresses all other content. At first I concentrated on getting the grouping right. The contents of the rest of the document should of course be visible, but I don't think that's a major problem.

With this template I only get all elements in a large group.

<modul>
    <list>
      <Aufzhlungbullets>some text</Aufzhlungbullets>
      <Aufzhlungbullets>some text</Aufzhlungbullets>
      <Aufzhlungbullets>some text</Aufzhlungbullets>
      <Aufzhlungbullets>some text</Aufzhlungbullets>
      <Aufzhlungbullets>some text</Aufzhlungbullets>
      <Aufzhlungbullets>some text</Aufzhlungbullets>
      <Aufzhlungbullets>some text</Aufzhlungbullets>
   </list>
</modul>

I tried a lot of variations of my template - without success. How can I differentiate that the wanted elements appear in different groups in the document?


Solution

  • Try using group-adjacent on the xsl:for-each-group instead, grouping adjacent nodes by their name, and wrapping the group in a <list> tag if it it a Aufzhlungbullets node:

    <xsl:template match="body">
        <list>
            <xsl:for-each-group select="*" group-adjacent="local-name()">
                <xsl:choose>
                    <xsl:when test="self::Aufzhlungbullets">
                        <list>
                            <xsl:copy-of select="current-group()" />    
                        </list>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="current-group()" />
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each-group>
        </list>
    </xsl:template>