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

Subject: [xsl] XPath selecting chain of following siblings of the same name
From: Kolací Tomáš <kolaci@xxxxxxx>
Date: Fri, 9 Mar 2007 14:29:29 +0100
 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>

Current Thread