xsl-list
[Top] [All Lists]

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

2007-03-10 04:43:51
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, Kolací Tomáš <kolaci(_at_)cncz(_dot_)cz> 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

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--