Re: [xsl] Selecting a subset of siblings using following-sibling

Subject: Re: [xsl] Selecting a subset of siblings using following-sibling
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Fri, 23 Nov 2001 19:10:26 +0000
Hi Liat,

> I need to extract the nodes B through D. I have tried to do this using:
> <xsl:for-each select="child::B/following-sibling::*[(name(.) != 'E')]">
>    do something here...
> </xsl:for-each>

That path selects the following siblings of the B element whose name
is not 'E' - everything after the B element that isn't an E element in
other words.

> but I seem to be getting the nodes B, C, D, F and G (which is not
> desirable, since I only need nodes B, C and D). I'm not sure if this
> is the right (read elegant) approach, but I can't figure out another
> way to select a subset of nodes and define its boundaries (i.e., is
> it possible to define the boundaries as 'upper' and 'lower' limits
> with node B being the 'lower' limit and node D the 'upper' limit?).

There are a few ways of approaching this. The obvious one is to select
the B, C and D elements explicitly with:

  <xsl:for-each select="B | C | D">

But I assume that you can't do that for some reason. Another approach
is to select all the child nodes of the current DETAIL element that
(a) have a B element as a preceding sibling, or are themselves a B
element and (b) have a D element as a following sibling, or are
themselves a D element. You can do this with:

  <xsl:for-each select="*[(preceding-sibling::B or self::B) and
                          (following-sibling::D or self::D)]">

Another method is to use templates rather than xsl:for-each and step
through the elements one by one. To start off, apply templates just to
the B element:

  <xsl:apply-templates select="B" />

Now have a template that matches all children of the DETAIL element
and does whatever you want it to do (the stuff inside the

<xsl:template match="DETAIL/*">

Once you've done whatever it is for that element, then you need to see
whether you need to apply templates to the next element. Basically you
apply templates to the next element as long as this element isn't the
D element (which is where you want the journey to stop):

<xsl:template match="DETAIL/*">
  <xsl:if test="not(self::D)">
    <xsl:apply-template select="following-sibling::*[1]" />

Using the template will probably be more efficient because it will
only visit the nodes that you're interested in, compared to the path
above which will visit *all* the children of the DETAIL element while
trying to work out whether it should process them or not.

I hope that helps,


Jeni Tennison

 XSL-List info and archive:

Current Thread