5
votes

I have been trying to write a XPath statement which will select the first preceding-sibling of a node. That sibling should have a particular node as its child.

For example:

<a>
    <c></c>
    ....
    <b></b>
</a>

<a></a>
....
....
<a></a>

<a>
   <b> start </b>
</a>

In the above XML, if my context is at the node a which has start text inside its child b. How can I select the preceding node a whose child is b?

I tried preceding-sibling::*[b][1] but it only selects the node a if b is its first child. I also tried preceding-sibling::*[1][b] but it only checks the first preceding a node and if it doesn't contains b, the test fails.

Note: I am using XSLT 1.0

Can anyone suggest me a solution?

Thnx in advance!!

3

3 Answers

8
votes

I believe that:

preceding-sibling::*[b][1]

or preferably:

preceding-sibling::a[b][1]

should work just fine.

I tried preceding-sibling::*[b][1] but it only selects the node a if b is its first child.

I don't think that is so.

5
votes

Like this. ("The first preceding sibling who has a <b> child"):

preceding-sibling::a[b][1]

or this ("The first preceding sibling but only if it has a <b> child"):

preceding-sibling::a[1][b]

Multiple predicates must be true one after another. Therefore, this:

preceding-sibling::a[b and position() = 1]

is equivalent to the second expression, but not to the first.

1
votes

With this input:

<root>
    <a>
        <c></c>
        <b>SUCCESS</b>
    </a>
    <a>FAIL</a>
    <a>FAIL</a>
    <a>
        <b> start </b>
    </a>
</root>

and this stylesheet

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" />
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="a[b[.=' start ']]">
        <xsl:copy>
        <xsl:value-of select="preceding-sibling::a[b]"/>
        </xsl:copy>

    </xsl:template>
</xsl:stylesheet>

it outputs

<root>
   <a>
      <c/>
      <b>SUCCESS</b>
   </a>
   <a>FAIL</a>
   <a>FAIL</a>
   <a>SUCCESS</a>
</root>