I want to get all ancestors of current node:
XML:
<root>
<item title="a">
<item title="b">
<item title="c"></item> <!--CURRENT-->
<item title="d"></item>
</item>
<item title="x">
<item title="y"></item>
<item title="z"></item>
</item>
</item>
</root>
Result:
<item title="a">...</item>
<item title="b">...</item>
Edit: Answers with axes ancestor are fine. My problem was elsewhere, in XSLT
XSLT:
<xsl:variable name="curr" select="//item[@title = 'c']"></xsl:variable>
<xsl:variable name="test" select="$curr/ancestor::item"></xsl:variable>
<xsl:for-each select="$test/item">
<xsl:value-of select="@title"></xsl:value-of>
</xsl:for-each>
Returns:
bcdx
Edit2: for dimitre and for all who have a similar problem
All the answers to my question were good.
Just XSLT (up) returns to me a strange result and @Mads Hansen corrected me.
FINAL WORKING EXAMPLE:
XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<item title="a">
<item title="b">
<item title="c"></item>
<item title="d"></item>
</item>
<item title="x">
<item title="y"></item>
<item title="z"></item>
</item>
</item>
</root>
XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="curr" select="//item[@title = 'c']"></xsl:variable>
<xsl:variable name="test" select="$curr/ancestor::item"></xsl:variable>
<xsl:for-each select="$test">
<xsl:value-of select="@title"></xsl:value-of>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Returns:
ab
Congradulations to Adam for a very quick first answer.
Your listed expected result does not match your words. the root element also an ancestor node and the document is also an ancestor node.
ancestor::node()
... will return a sequence in this order:
item[@title='b']
item[@title='a']
root
element (a.k.a. the document element)/
To get the specific result you listed, you need:
ancestor::item/.
The effect of the /. is to change the ordering back to forward document order. The native order of ancestor:: is reverse document order.
This style-sheet (with OP's input)...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:for-each select="//item[@title='c']">
<xsl:value-of select="ancestor::item[1]/@title" />
<xsl:value-of select="ancestor::item[2]/@title" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
... will output 'ba' illustrating the point that ancestor:: is indeed a reverse axis. And yet this style-sheet ...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:for-each select="//item[@title='c']">
<xsl:value-of select="(ancestor::item/@title)[1]" />
<xsl:value-of select="(ancestor::item/@title)[2]" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
... has the opposite result 'ab' . This is instructive because it shows that in XSLT 1.0 (not so in XSLT 2.0), the brackets remove the reverse nature, and it becomes a document ordered node-set.
The OP has asked about a transform something like....
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:for-each select="//item[@title='c']">
<xsl:for-each select="ancestor::item">
<xsl:value-of select="@title" />
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This one returns 'ab' (in XSLT 2.0 it would return 'ba'). Why? Because in XSLT 1.0, the xsl:for-each instruction ignores the reverse-ness of the axis and processes in document order (unless an xsl:sort instruction says otherwise).
I want to get all ancestors of current node
The designated nodes obviously aren't all ancestors of the "green" node.
To select all ancestors use:
ancestor::node()
This selects all ancestor nodes of the initial conext node (or current node), including the top element and its parent -- the root node /
-- which is a node, but isn't an element.
To select all element ancestors use:
ancestor::*
This is similar to the previous expression, but doesn't select the root node (/
), because it isn't an element.
To select all ancestors named item
, use:
ancestor::item
Do Note: All expressions above assume that (//item[@title='c'])[1]
is the initial context node (current node). If this assumption is not correct, then (//item[@title='c'])[1]/
must be prepended to each of the expressions.
Note2: I strongly recommend for learning XPath to use a tool like the XPath Visualizer. For many years this tool has helped many thousands of people learn XPath the fun way -- by playing with XPath expressions and observing the result of their evaluation.
Note: The XPath Visualizer was created by me in year 2000 and was never a financial product. I am recommending it based solely on its value to the users, proven in the course of many years
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With