XPath ancestor and descendant in XSL copy-of
I am new to XPath, and from what I have read in some tutorials about axes, I am still left wondering how to implement them. They aren't quite behaving as I had expected. I am particularly interested in using ancestor and descendant axes.
I have the following XML structure:
<file>
<criteria>
<root>ROOT</root>
<criterion>AAA</criterion>
<criterion>BBB</criterion>
<criteri开发者_JS百科on>CCC</criterion>
</criteria>
<format>
<sort>BBB</sort>
</format>
</file>
And I have the following XSL:
<xsl:template match="/">
<xsl:copy-of select="ancestor::criterion/>
</xsl:template>
which produces nothing!
I expected it to produce:
<file>
<criteria>
</criteria>
</file>
Can someone explain ancestor and descendant axes to me in a more helpful way than the tutorials I have previously read?
Thanks!
And I have the following XSL:
<xsl:template match="/"> <xsl:copy-of select="ancestor::criterion/> </xsl:template>
which produces nothing!
As it should!
ancestor::criterion
is a relative expression, which means that it is evaluated off the current node (matched by the template). But the current node is the document node /
.
So, the above is equivalent to:
/ancestor::criterion
However, by definition the document node /
has no parents (and that means no ancestors), so this XPath expression doesn't select any node.
I expected it to produce:
<file> <criteria> </criteria> </file>
What you probably wanted was:
//criterion/ancestor::*
or
//*[descendant::criterion]
The last two XPath expressions are equivalent and select all elements that have a criterion
descendant.
Finally, to produce the output you wanted, here is one possible solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root | criterion | format"/>
</xsl:stylesheet>
When this transformation is applied on the provided XML document, the wanted output is produced:
<file>
<criteria>
</criteria>
</file>
ancestor
is for selecting nodes that are higher (closer to the root) in the XML document. descendant
is for selecting nodes that are lower (children) in the XML document.
In your example, ancestor::criterion
selects nothing because the current node is /
(meaning the root of the document - <file>
in this case), as indicated by match="/"
. The root node has no ancestors so the ancestor
axis does nothing.
To get every <criterion>
element, you should use the descendant
axis:
<xsl:template match="/">
<xsl:copy-of select="descendant::criterion"/>
</xsl:template>
Or its shortcut //
:
<xsl:template match="/">
<xsl:copy-of select="//criterion"/>
</xsl:template>
That will return the following:
<criterion>AAA</criterion>
Using a loop or another template you can get all three of them:
<xsl:template match="/">
<file>
<xsl:apply-templates select="//criterion"/>
</file>
</xsl:template>
<xsl:template match="criterion">
<xsl:copy-of select="."/>
</xsl:template>
This will produce the following:
<file>
<criterion>AAA</criterion>
<criterion>BBB</criterion>
<criterion>CCC</criterion>
</file>
If you want to get the <file>
element, too, it's a bit more complicated. XPath specifies nodes and simple copies will not copy the elements that contain the elements you select. I can clarify this point more if you're still confused.
精彩评论