Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XPath - Get node with no child of specific type

Tags:

xpath

XML: /A/B or /A

I want to get all A nodes that do not have any B children.

I've tried

/A[not(B)]  
/A[not(exists(B))]

without success

I prefer a solution with the syntax /*[local-name()="A" and .... ], if possible. Any ideas that works?

Clarification. The xml looks like:

<WhatEver>
  <A>
    <B></B>
  </A>
</WhatEver> 

or

<WhatEver>
  <A></A>
</WhatEver>
like image 231
Martin Bring Avatar asked May 14 '09 08:05

Martin Bring


People also ask

What is the difference between child and descendant in XPath?

child:: are your immediate children. descendant:: are your children, and their children, recursively.

What is child :: In XPath?

As defined in the W3 XPath 1.0 Spec, " child::node() selects all the children of the context node, whatever their node type." This means that any element, text-node, comment-node and processing-instruction node children are selected by this node-test.

How do you specify a child element using XPath?

For the div element with an id attribute of hero //div[@id='hero'] , these XPath expression will select elements as follows: //div[@id='hero']/* will select all of its children elements. //div[@id='hero']/img will select all of its children img elements. //div[@id='hero']//* will select all of its descendent elements.

How can I get parent node in XPath?

A Parent of a context node is selected Flat element. A string of elements is normally separated by a slash in an XPath statement. You can pick the parent element by inserting two periods “..” where an element would typically be. The parent of the element to the left of the double period will be selected.


3 Answers

Maybe *[local-name() = 'A' and not(descendant::*[local-name() = 'B'])]?

Also, there should be only one root element, so for /A[...] you're either getting all your XML back or none. Maybe //A[not(B)] or /*/A[not(B)]?

I don't really understand why /A[not(B)] doesn't work for you.

~/xml% xmllint ab.xml
<?xml version="1.0"?>
<root>
    <A id="1">
            <B/>
    </A>
    <A id="2">
    </A>
    <A id="3">
            <B/>
            <B/>
    </A>
    <A id="4"/>
</root>
~/xml% xpath ab.xml '/root/A[not(B)]'
Found 2 nodes:
-- NODE --
<A id="2">
    </A>
-- NODE --
<A id="4" />
like image 114
alamar Avatar answered Oct 16 '22 07:10

alamar


Try this "/A[not(.//B)]" or this "/A[not(./B)]".

like image 30
Serhiy Avatar answered Oct 16 '22 07:10

Serhiy


The first / causes XPath to start at the root of the document, I doubt that is what you intended.

Perhaps you meant //A[not(B)] which would find all A nodes in the document at any level that do not have a direct B child.

Or perhaps you are already at a node that contains A nodes in which case you just want A[not(B)] as the XPath.

like image 34
AnthonyWJones Avatar answered Oct 16 '22 07:10

AnthonyWJones