Re: [xsl] Grouping simple flat structure

Subject: Re: [xsl] Grouping simple flat structure
From: "Andrew Welch" <andrew.j.welch@xxxxxxxxx>
Date: Mon, 17 Mar 2008 13:01:00 +0000
On 17/03/2008, Ian Proudfoot <ian.proudfoot@xxxxxxxxxxx> wrote:
> I have a blind spot when it comes to grouping flat XML structure using XSLT
>  1.0.
>
>  My source XML structure is typically as follows (simplified for clarity):
>
>  <section>
>   <title/>
>   <warning/>
>   <warning/>
>   <para/>
>   <warning/>
>   <warning/>
>   <para/>
>   <para/>
>  </section>
>
>  I would like to group adjacent <warning> elements to give this structure:
>
>  <section>
>   <title/>
>   <w-group>
>     <warning/>
>     <warning/>
>   </w-group>
>   <para/>
>   <w-group>
>     <warning/>
>     <warning/>
>   </w-group>
>   <para/>
>   <para/>
>  </section>
>
>  I've tried using Muenchian Grouping, but I'm going wrong somewhere. I
>  started by trying to identify the start of each group using <xsl:template
>  match="warning[(preceding-sibling::*[1])[not(self::warning)]]">... As a
>  starting point is this correct, it does seem to capture the first warning in
>  each group? After that I cannot grab the following <warning> sibling
>  elements correctly.

You can use the "sibling-recursion" template (also known as the
"modified identity tempate") which walks the sibling axis one node at
time:

   <xsl:template match="@* | node()">
      <xsl:copy>
         <xsl:apply-templates select="@*"/>
         <xsl:apply-templates select="node()[1]"/>
      </xsl:copy>
      <xsl:apply-templates select="following-sibling::node()[1]"/>
   </xsl:template>

Then have a specific template to process <warning> elements - output
the container element and then process the warning using a special
moded template to differentiate it from this template.  The next
non-warning element is processed after the container element.

  <xsl:template match="warning">
	    <w-group>
		      <xsl:apply-templates select="." mode="copy"/>		
	    </w-group>
	    <xsl:apply-templates
select="(following-sibling::*[not(self::warning)])[1]"/>
  </xsl:template>

This is the template that effectively groups the warnings - it copies
the current warning (or probably processes it in your real transform)
and then processes the next node as long as it's a warning:

    <xsl:template match="warning" mode="copy">
	    <xsl:copy-of select="."/>	
	    <xsl:apply-templates
select="following-sibling::*[1][self::warning]" mode="copy"/>
    </xsl:template>


cheers
-- 
Andrew Welch
http://andrewjwelch.com
Kernow: http://kernowforsaxon.sf.net/

Current Thread