Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XPath ancestor and descendant in XSL copy-of

Tags:

xml

xslt

xpath

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>
        <criterion>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!

like image 362
developer Avatar asked Jun 01 '10 18:06

developer


1 Answers

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>
like image 98
Dimitre Novatchev Avatar answered Sep 27 '22 15:09

Dimitre Novatchev