开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜