Search code examples
xmlxpathancestor

Obtain nodes that don't have specific ancestor xml xpath


I would like to have xpath, that obtains nodes, that don't have ancestor, which is first descendant of specific node.

Let's assume we have xml document like this:

<a>
  <b>This node</b>
  <c>
    <a>
      <b>not this</b>
      <g>
        <b>not this</b>
      </g>
    </a>
    <a>
      <b>This node</b>
      <c/>
    </a>
  </c>
</a>


<a>
  <c>
    <a>
      <b>not this</b>
    </a>
    <a>
      <b>This node</b> 
    </a>
    <a>
      <b>This node</b> 
    </a>
    <a>
      <b>This node</b> 
    </a>
  </c>
</a>


<d>
  <b>This node</b>
</d>

I would like to select all b nodes in document that don't have as their ancestor node //a/c/a[1].


Solution

  • I would like to select all b nodes in document that don't have as their ancestor node //a/c/a[1]

    Use this XPath expression:

    //b[not(ancestor::a
                 [parent::c[parent::a]
                and
                  not(preceding-sibling::a)
                 ]
           )
       ]
    

    This selects all b elements in the document that don't have ancestor a that has a parent c that has parent a and the a ancestor that has parent c is not the first a child of its parent.

    Given the following XML document (based on the provided, but made well-formed and also put identifying text in the nodes that should be selected):

    <t>
        <a>
            <b>This node 1</b>
            <c>
                <a>
                    <b>not this</b>
                    <g>
                        <b>not this</b>
                    </g>
                </a>
                <a>
                    <b>This node 2</b>
                    <c/>
                </a>
            </c>
        </a>
        <a>
            <c>
                <a>
                    <b>not this</b>
                </a>
                <a>
                    <b>This node 3</b>
                </a>
                <a>
                    <b>This node 4</b>
                </a>
                <a>
                    <b>This node 5</b>
                </a>
            </c>
        </a>
        <d>
            <b>This node 6</b>
        </d>
    </t>
    

    exactly the wanted 6 b elements are selected.

    Verification using XSLT:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/">
     <xsl:copy-of select=
     "//b[not(ancestor::a
                 [parent::c[parent::a]
                and
                  not(preceding-sibling::a)
                 ]
             )
         ]
    
     "/>
     </xsl:template>
    </xsl:stylesheet>
    

    when this transformation is applied on the above XML document, exactly the wanted b elements are selected and copied to the output. The wanted, correct result is produced:

    <b>This node 1</b>
    <b>This node 2</b>
    <b>This node 3</b>
    <b>This node 4</b>
    <b>This node 5</b>
    <b>This node 6</b>