How to get parent element with attribute using xpath - xpath

I have posted sample XML and expected output kindly help to get the result.
Sample XML
<root>
<A id="1">
<B id="2"/>
<C id="2"/>
</A>
</root>
Expected output:
<A id="1"/>

You can formulate this query in several ways:
Find elements that have a matching attribute, only ascending all the time:
//*[#id=1]
Find the attribute, then ascend a step:
//#id[.=1]/..
Use the fn:id($id) function, given the document is validated and the ID-attribute is defined as such:
/id('1')

I think it's not possible what you're after. There's no way of selecting a node without its children using XPATH (meaning that it'd always return the nodes B and C in your case)
You could achieve this using XQuery, I'm not sure if this is what you want but here's an example where you create a new node based on an existing node that's stored in the $doc variable.
declare variable $doc := <root><A id="1"><B id="2"/><C id="2"/></A></root>;
element {fn:node-name($doc/*)} {$doc/*/#*}
The above returns <A id="1"></A>.

is that what you are looking for?
//*[#id='1']/parent::* , similar to //*[#id='1']/../
if you want to verify that parent is root :
//*[#id='1']/parent::root
https://en.wikipedia.org/wiki/XPath
if you need not just parent - but previous element with some attribute: Read about Axis specifiers and use Axis "ancestor::" =)

Related

Combining the use of preceding- and following-sibling in the same XPath query up to first instance

Following the post here Combining the use of preceding and following sibling in the same xpath query I am wanting to do the same except I want to get nodes up to the first occurrence of p2.
So the modified example data is
<a>
<b property="p1">zyx</b>
<b>wvu</b>
<b>tsr</b>
<b>dcv</b>
<b property="p2">qpo</b>
<b>qcs</b>
<b property="p2">wee</b>
<b>tbg</b>
<b>rty</b>
<b property="p2">qwe</b>
<b>jkl</b>
</a>
I have tried
/a/b[preceding-sibling::b/#property='p1' and following-sibling::b[1]/#property='p2']
which gives me dcv, qcs, and rty.
Then I tried
/a/b[preceding-sibling::b/#property='p1' and (following-sibling::b/#property='p2')[1]]
which gives me wvu, tsr, dcv, qpo, qcs, wee, tbg, and rty.
What I actually need is from p1 to the first p2 or wvu, tsr, and dcv. I have tried almost every combination of [1] but have failed and would like some help on the construct.
One way to achieve this is by using the following XPath-1.0 expression:
/a/b[preceding-sibling::b/#property='p1' and not(preceding-sibling::b/#property='p2' or #property='p2')]
It checks if a given b element has a preceding-sibling named p1 and is not a preceding-sibling of p2 or p2 itself. That's all.

xpath without specificy the tag? [duplicate]

Given this XML, what XPath returns all elements whose prop attribute contains Foo (the first three nodes):
<bla>
<a prop="Foo1"/>
<a prop="Foo2"/>
<a prop="3Foo"/>
<a prop="Bar"/>
</bla>
//a[contains(#prop,'Foo')]
Works if I use this XML to get results back.
<bla>
<a prop="Foo1">a</a>
<a prop="Foo2">b</a>
<a prop="3Foo">c</a>
<a prop="Bar">a</a>
</bla>
Edit:
Another thing to note is that while the XPath above will return the correct answer for that particular xml, if you want to guarantee you only get the "a" elements in element "bla", you should as others have mentioned also use
/bla/a[contains(#prop,'Foo')]
This will search you all "a" elements in your entire xml document, regardless of being nested in a "blah" element
//a[contains(#prop,'Foo')]
I added this for the sake of thoroughness and in the spirit of stackoverflow. :)
This XPath will give you all nodes that have attributes containing 'Foo' regardless of node name or attribute name:
//attribute::*[contains(., 'Foo')]/..
Of course, if you're more interested in the contents of the attribute themselves, and not necessarily their parent node, just drop the /..
//attribute::*[contains(., 'Foo')]
descendant-or-self::*[contains(#prop,'Foo')]
Or:
/bla/a[contains(#prop,'Foo')]
Or:
/bla/a[position() <= 3]
Dissected:
descendant-or-self::
The Axis - search through every node underneath and the node itself. It is often better to say this than //. I have encountered some implementations where // means anywhere (decendant or self of the root node). The other use the default axis.
* or /bla/a
The Tag - a wildcard match, and /bla/a is an absolute path.
[contains(#prop,'Foo')] or [position() <= 3]
The condition within [ ]. #prop is shorthand for attribute::prop, as attribute is another search axis. Alternatively you can select the first 3 by using the position() function.
Have you tried something like:
//a[contains(#prop, "Foo")]
I've never used the contains function before but suspect that it should work as advertised...
John C is the closest, but XPath is case sensitive, so the correct XPath would be:
/bla/a[contains(#prop, 'Foo')]
If you also need to match the content of the link itself, use text():
//a[contains(#href,"/some_link")][text()="Click here"]
/bla/a[contains(#prop, "foo")]
try this:
//a[contains(#prop,'foo')]
that should work for any "a" tags in the document
For the code above...
//*[contains(#prop,'foo')]

Xpath get element above

suppose I have this structure:
<div class="a" attribute="foo">
<div class="b">
<span>Text Example</span>
</div>
</div>
In xpath, I would like to retrieve the value of the attribute "attribute" given I have the text inside: Text Example
If I use this xpath:
.//*[#class='a']//*[text()='Text Example']
It returns the element span, but I need the div.a, because I need to get the value of the attribute through Selenium WebDriver
Hey there are lot of ways by which you can figure it out.
So lets say Text Example is given, you can identify it using this text:-
//span[text()='Text Example']/../.. --> If you know its 2 level up
OR
//span[text()='Text Example']/ancestor::div[#class='a'] --> If you don't know how many level up this `div` is
Above 2 xpaths can be used if you only want to identify the element using Text Example, if you don't want to iterate through this text. There are simple ways to identify it directly:-
//div[#class='a']
From your question itself you have mentioned the answer for it
but I need the div.a,
try this
driver.findElement(By.cssSelector("div.a")).getAttribute("attribute");
use cssSelector for best result.
or else try the following xpath
//div[contains(#class, 'a')]
If you want attribute of div.a with it's descendant span which contains text something, try as below :-
driver.findElement(By.xpath("//div[#class = 'a' and descendant::span[text() = 'Text Example']]")).getAttribute("attribute");
Hope it helps..:)

How to return xpath union of nodes in separate trees?

It's a basic question, but I couldn't find the answer anywhere.
<a>
<b1>
<d1>D1</d1>
<e1>E1</e1>
</b1>
<b2>
<c2>
<d2>D2</d2>
<e2>E2</e2>
</c2>
</b2>
</a>
From the above I'd like to return:
<a>
<d1>D1</d1>
<e1>E1</e1>
<d2>D2</d2>
<e2>E2</e2>
</a>
And not:
<a>
<b1>
<d1>D1</d1>
<e1>E1</e1>
</b1>
<b2>
<d2>D2</d2>
<e2>E2</e2>
</b2>
</a>
If that makes any sense. I tried "/a", but that gave me:
<a>
<b1>
<d1>D1</d1>
<e1>E1</e1>
</b1>
<b2>
<c2>D2E2</c2>
</b2>
</a>
If you meant to select all leave nodes (nodes without child node(s)), you can try this XPath :
//*[not(*)]
Or using XPath union (|) to get child nodes of <b1> and <c2> :
(//b1/* | //c2/*)
Given sample XML you posted, both XPath above will return :
<d1>D1</d1>
<e1>E1</e1>
<d2>D2</d2>
<e2>E2</e2>
But if you really need the result to be wrapped in <a>, then I agree with #minopret comment, that isn't what XPath meant to do. XSLT is more proper way to transform an XML to different format.
UPDATE :
In respond to your last comment, there is no such grouping in XPath. Should be done in the host language if you need that data structure. Your best bet is to select parent of those desired nodes in XPath so you get them grouped by their parent. Then you can do further processing in the host language, for example :
//*[not(*)]/parent::*
//*[*[not(*)]]
Any of above two XPath queries can return :
<b1>
<d1>D1</d1>
<e1>E1</e1>
</b1>
<c2>
<d2>D2</d2>
<e2>E2</e2>
</c2>
XPath can only return nodes that are already present in your source tree. To construct new nodes, or reorganise the tree, you need XSLT or XQuery.

XPath - Get node with no child of specific type

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>
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" />
Try this "/A[not(.//B)]" or this "/A[not(./B)]".
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.
If you are trying to get A anywhere in the hierarchy from the root, this works (for xslt 1.0 as well as 2.0 in case its used in xslt)
//descendant-or-self::node()[local-name(.) = 'a' and not(count(b))]
OR you can also do
//descendant-or-self::node()[local-name(.) = 'a' and not(b)]
OR also
//descendant-or-self::node()[local-name(.) = 'a' and not(child::b)]
There are n no of ways in xslt to achieve the same thing.
Note: XPaths are case-sensitive, so if your node names are different (which I am sure, no one is gonna use A, B), then please make sure the case matches.
Use this:
/*[local-name()='A' and not(descendant::*[local-name()='B'])]

Resources