Re: [xsl] XPath selecting chain of following siblings of the same name

Subject: Re: [xsl] XPath selecting chain of following siblings of the same name
From: "Mukul Gandhi" <gandhi.mukul@xxxxxxxxx>
Date: Sat, 10 Mar 2007 17:13:30 +0530
I wonder, why this XSLT 1.0 solution doesn't work:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
version="1.0">

<xsl:output method="xml" indent="yes"/>

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

<xsl:template match="a[not(preceding-sibling::*[1][self::a])]">
  <a-block>
    <xsl:copy-of select="." />
    <xsl:apply-templates select="following-sibling::*[1][self::a]" />
  </a-block>
</xsl:template>

</xsl:stylesheet>

I have tried to code the sibling-recursion technique for "positional
grouping" suggested by Michael Kay long time ago.

I'll be grateful if anyone let me know what's wrong with the above code.

On 3/9/07, Kolacm Toma <kolaci@xxxxxxx> wrote:
Hi!

I have following input XML:

 <root>
   <a id="1"/>
   <a id="2"/>
   <b/>
   <d/>
   <g/>
   <a id="3"/>
   <a id="4"/>
   <a id="5"/>
   <x/>
   <a id="6"/>
   <a id="7"/>
 </root>

and I'm trying to create XSLT 1.0 script, which would nest "uninterrupted"
sibling groups of 'a' elements into 'a-block' elements, so the output would
look like:

<root> <a-block> <a id="1"/> <a id="2"/> </a-block> <b/> <d/> <g/> <a-block> <a id="3"/> <a id="4"/> <a id="5"/> </a-block> <x/> <a-block> <a id="6"/> <a id="7"/> </a-block> </root>

How should look XPath selecting next 'a' elements of the same group in
template matching first group element?

I was able to create this (see second xsl:copy-of) (derived from
http://www.biglist.com/lists/xsl-list/archives/200208/msg01182.html idea):

<xsl:template match="a"> <xsl:if test="not(preceding-sibling::*[1][self::a])"> <!-- first node of
each group -->
     <xsl:element name="a-block">
       <xsl:copy-of select="."/>
       <xsl:copy-of select="following-sibling::a
         [preceding-sibling::*[1][self::a]]

[generate-id(preceding-sibling::a[not(preceding-sibling::*[1][self::a])][1]) =
generate-id(current())]"/>
         <!-- first condition selects only 2nd+ nodes of each group -->
         <!-- second condition checks if the 1st node of the group is the
current one -->
     </xsl:element>
   </xsl:if>
 </xsl:template>

and it works (as far as I have test it up to now), but it's ugly, there must
be something simpler..?

Thanks in advance!


Best regards, Tomas Kolaci
____________________

Full script (it does not handle white spaces correctly, but it does not
matter in this case):

<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="1.0">

<xsl:output method="xml" indent="no"/>

 <xsl:template match="root">
   <xsl:copy>
     <xsl:apply-templates/>
   </xsl:copy>
 </xsl:template>

 <xsl:template match="a">
   <xsl:if test="not(preceding-sibling::*[1][self::a])"> <!-- first node of
each group -->
     <xsl:element name="a-block">
       <xsl:copy-of select="."/>
       <xsl:copy-of select="following-sibling::a
         [preceding-sibling::*[1][self::a]]

[generate-id(preceding-sibling::a[not(preceding-sibling::*[1][self::a])][1]) =
generate-id(current())]"/>
         <!-- first condition selects only 2nd+ nodes of each group -->
         <!-- second condition checks if the 1st node of the group is the
current one -->
     </xsl:element>
   </xsl:if>
 </xsl:template>

 <xsl:template match="*">
   <xsl:copy-of select="."/>
 </xsl:template>

</xsl:stylesheet>


--
Regards,
Mukul Gandhi

Current Thread