Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to select these elements with Xpath?

Tags:

xslt

xpath

I have a document, something like this:

<root>
   <A node="1"/>
   <B node="2"/>
   <A node="3"/>
   <A node="4"/>
   <B node="5"/>
   <B node="6"/>
   <A node="7"/>
   <A node="8"/>
   <B node="9"/>
</root>

Using xpath, How can I select all B elements that consecutively follow a given A element?

It's something like following-silbing::B, except I want them to be only the immediately following elements.

If I am on A (node==1), then I want to select node 2. If I am on A (node==3), then I want to select nothing. If I am on A (node==4), then I want to select 5 and 6.

Can I do this in xpath? EDIT: It is within an XSL stylesheet select statement.


EDIT2: I don't want to use the node attribute on the various elements as a unique identifier. I included the node attribute only for purposes of illustrating my point. In the actual XML doc, I don't have an attribute that I use as a unique identifer. The xpath "following-sibling::UL[preceding-sibling::LI[1]/@node = current()/@node]" keys on the node attribute, and that's not what I want.

like image 601
Cheeso Avatar asked Jan 23 '23 10:01

Cheeso


2 Answers

Short answer (assuming current() is ok, since this is tagged xslt):

following-sibling::B[preceding-sibling::A[1]/@node = current()/@node]

Example stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"/>
    <xsl:template match="/">
        <xsl:apply-templates select="/root/A"/>
    </xsl:template>

    <xsl:template match="A">
        <div>A: <xsl:value-of select="@node"/></div>
        <xsl:apply-templates select="following-sibling::B[preceding-sibling::A[1]/@node = current()/@node]"/>
    </xsl:template>

    <xsl:template match="B">
        <div>B: <xsl:value-of select="@node"/></div>
    </xsl:template>
</xsl:stylesheet>

Good luck!

like image 107
Chris Nielsen Avatar answered Feb 01 '23 11:02

Chris Nielsen


While @Chris Nielsen's answer is the right approach, it leaves an uncertainty in cases where the compared attribute is not unique. The more correct way of solving this is:

following-sibling::B[
  generate-id(preceding-sibling::A[1]) = generate-id(current())
]

This makes sure that the preceding-sibling::A is identical to the current A, instead of just comparing some attribute values. Unless you have attributes that are guaranteed to be unique, this is the only safe way.

like image 28
Tomalak Avatar answered Feb 01 '23 11:02

Tomalak