Note that there are some explanatory texts on larger screens.

plurals
  1. POProblem using ancestor axis in an XPath predicate
    primarykey
    data
    text
    <p>Using this source document:</p> <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;Root&gt; &lt;Element1 id="UniqueId1"&gt; &lt;SubElement1/&gt; &lt;SubElement2&gt; &lt;LeafElement1/&gt; &lt;LeafElement1/&gt; &lt;/SubElement2&gt; &lt;/Element1&gt; &lt;Element2 id="UniqueId2" AttributeToCheck="true"&gt; &lt;SubElement1&gt; &lt;LeafElement1/&gt; &lt;LeafElement1/&gt; &lt;/SubElement1&gt; &lt;/Element2&gt; &lt;/Root&gt; </code></pre> <p>I want to add the attribute foo="bar" to elements that both:</p> <ol> <li>Have sibling elements with the same name</li> <li>Have any ancestor with attribute AttributeToCheck</li> </ol> <p>This should be the result: </p> <pre><code>&lt;?xml version="1.0"?&gt; &lt;Root&gt; &lt;Element1 id="UniqueId1"&gt; &lt;SubElement1/&gt; &lt;SubElement2&gt; &lt;LeafElement1/&gt; &lt;LeafElement1/&gt; &lt;/SubElement2&gt; &lt;/Element1&gt; &lt;Element2 id="UniqueId2" AttributeToCheck="true"&gt; &lt;SubElement1&gt; &lt;LeafElement1 foo="bar"/&gt; &lt;LeafElement1 foo="bar"/&gt; &lt;/SubElement1&gt; &lt;/Element2&gt; &lt;/Root&gt; </code></pre> <p>This is my stlesheet so far. It adds the attribute elements matching condition 1 but fails to account for condition 2. </p> <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt; &lt;xsl:output method="xml" version="1.0" indent="yes"/&gt; &lt;xsl:template match="* | @*"&gt; &lt;xsl:copy&gt; &lt;xsl:apply-templates select="* | @*"/&gt; &lt;/xsl:copy&gt; &lt;/xsl:template&gt; &lt;xsl:template match="*[count(../*[name(.) = name(current())]) &gt; 1]"&gt; &lt;xsl:copy&gt; &lt;xsl:attribute name="foo"&gt;bar&lt;/xsl:attribute&gt; &lt;xsl:apply-templates select="* | @*"/&gt; &lt;/xsl:copy&gt; &lt;/xsl:template&gt; &lt;/xsl:stylesheet&gt; </code></pre> <p>The incorrect output: </p> <pre><code>&lt;?xml version="1.0"?&gt; &lt;Root&gt; &lt;Element1 id="UniqueId1"&gt; &lt;SubElement1/&gt; &lt;SubElement2&gt; &lt;LeafElement1 foo="bar"/&gt; (incorrect) &lt;LeafElement1 foo="bar"/&gt; (incorrect) &lt;/SubElement2&gt; &lt;/Element1&gt; &lt;Element2 id="UniqueId2" AttributeToCheck="true"&gt; &lt;SubElement1&gt; &lt;LeafElement1 foo="bar"/&gt; (correct) &lt;LeafElement1 foo="bar"/&gt; (correct) &lt;/SubElement1&gt; &lt;/Element2&gt; &lt;/Root&gt; </code></pre> <p>Since the second template already correctly matches elements that have siblings with the same name, it should be easy to use the ancestor XPath axis to exclude elements without AttributeToCheck ancestors. I added another predicate to the second template. </p> <pre><code>&lt;xsl:template match="*[ancestor::*[@AttributeToCheck]][count(../*[name(.) = name(current())]) &gt; 1]"&gt; </code></pre> <p>When I apply this stylesheet, the output document is the same as the input document, showing that the second template doesn't match any elements. I also tried changing the new predicate to use the node count. </p> <pre><code>&lt;xsl:template match="*[count(ancestor::*[@AttributeToCheck]) &gt; 0][count(../*[name(.) = name(current())]) &gt; 1]"&gt; </code></pre> <p>This also didn't work, the output document was the same as the input document. This is surprising, because when I use this ancestor expression to output the name of nodes with AttributeToCheck it works. I made these changes to the sylesheet: </p> <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt; &lt;xsl:output method="xml" version="1.0" indent="yes"/&gt; &lt;xsl:template match="* | @*"&gt; &lt;xsl:copy&gt; &lt;xsl:apply-templates select="* | @*"/&gt; &lt;/xsl:copy&gt; &lt;/xsl:template&gt; &lt;xsl:template match="*[count(../*[name(.) = name(current())]) &gt; 1]"&gt; &lt;xsl:copy&gt; &lt;xsl:attribute name="foo"&gt;bar&lt;/xsl:attribute&gt; &lt;xsl:attribute name="AncestorCount"&gt; &lt;xsl:value-of select="count(ancestor::*[@AttributeToCheck])"/&gt; &lt;/xsl:attribute&gt; &lt;xsl:attribute name="AncestorName"&gt; &lt;xsl:value-of select="name(ancestor::*[@AttributeToCheck])"/&gt; &lt;/xsl:attribute&gt; &lt;xsl:apply-templates select="* | @*"/&gt; &lt;/xsl:copy&gt; &lt;/xsl:template&gt; &lt;/xsl:stylesheet&gt; </code></pre> <p>Produces this output:</p> <pre><code>&lt;?xml version="1.0"?&gt; &lt;Root&gt; &lt;Element1 id="UniqueId1"&gt; &lt;SubElement1/&gt; &lt;SubElement2&gt; &lt;LeafElement1 foo="bar" AncestorCount="0" AncestorName=""/&gt; &lt;LeafElement1 foo="bar" AncestorCount="0" AncestorName=""/&gt; &lt;/SubElement2&gt; &lt;/Element1&gt; &lt;Element2 id="UniqueId2" AttributeToCheck="true"&gt; &lt;SubElement1&gt; &lt;LeafElement1 foo="bar" AncestorCount="1" AncestorName="Element2"/&gt; &lt;LeafElement1 foo="bar" AncestorCount="1" AncestorName="Element2"/&gt; &lt;/SubElement1&gt; &lt;/Element2&gt; &lt;/Root&gt; </code></pre> <p>My question is, why does the XPath predicate <code>*[ancestor::*[@AttributeToCheck]][count(../*[name(.) = name(current())]) &gt; 1]</code> not correctly match elements matching both conditions 1 and 2? What XPath expression should I use instead? </p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload