XPath Get first element of subset - xpath

I have XML like this:
<AAA>
<BBB aaa="111" bbb="222">
<CCC/>
<CCC xxx="555" yyy="666" zzz="777"/>
</BBB>
<BBB aaa="999">
<CCC xxx="qq"/>
<DDD xxx="ww"/>
<EEE xxx="oo"/>
</BBB>
<BBB>
<DDD xxx="oo"/>
</BBB>
</AAA>
I want to get first <CCC> element. But with XPath expression //*/CCC[1] I have got two <CCC> elements. Each of them is the first elemet in <BBB></BBB> context. How to get first element in subset?

This one should work for you:
(//*/CCC)[1]

I want to get first element. But with
XPath expression //*/CCC[1] I have
got two elements. Each of them is the
first elemet in <BBB></BBB> context.
How to get first element in subset?
This is a FAQ:
The [] operator has a higher precedence (binds stronger) than the // abbreviation.
Use:
(//CCC)[1]
This selects the first (in document order) CCC element in the XML document.

Related

Xpath expression (nokogiri) to get tag's child element?

From my xml, I can get this :
<home>
<creditors>
<count>2</count>
</creditors>
</home>
OR even this :
<home>
<creditors>
<moreThan>2</moreThan>
</creditors>
</home>
Which xpath expression can I use to get "<count>2</count>" instead of getting only "2" OR to get "<moreThan>2</moreThan>" instead of getting "2" ?
This XPath,
//creditors/count
will select all count child elements of all creditors elements in the XML document.
Update per OP's request in comments for a single XPath that selects both count and moreThan elements:
This XPath,
//creditors/*[self::count or self::moreThan]
will select all count or moreThan child elements of all creditors elements in the XML document.
Assuming that your xpath expression is OK, you just need to convert the element to string:
doc.xpath("home/creditors/*").to_s
=> "<count>2</count>"
Please check with queries returning more than one element, to make sure that it's desired behaviour.

Nested xpath: How do I use the result of an XPath expression as value?

I am having the following XML structure:
<xml>
<value>b</value>
<objects>
<object>
<value>a</value>
</object>
<object>
<value>b</value>
</object>
</objects>
</xml>
What I want is to select the second object, based on the value in the xml.
This XPath works:
//xml/objects/object[value = 'b']
This XPath does not return results:
//xml/objects/object[value = //xml/value/text()]
Are nested XPath expressions not supported?
They are, but the search within a predicate is always relative to the context you currently in.
Currently you start looking for an <xml/> element which is a child of <object/> and as there is none it will yield an empty result set.
Using ../ or parent::* you can go an axis step up to the parent and can select the required value:
//xml/objects/object[value = ../../value]

how to match no following sibling

Here's my xml,
<w:tc>
<w:p>
<w:pPr></w:pPr>
<w:r></w:r>
</w:p>
</w:tc>
<w:tc>
<w:p>
<w:pPr></w:pPr>
</w:p>
</w:tc>
I want to match w:p which is preceded by w:tc and has no following sibling w:r, Precisely i want second w:tc. Code what i have tried,
<xsl:template match="w:pPr[ancestor::w:p[ancestor::w:tc] and not(following-sibling::w:r)]">
I need xpath for w:pPr having no following-sibling
The problem is when w:pPr is followed by w:hyperlink. Now i have ignored w:hyperlink too.
If you want to match a w:pPr that has no following sibling elements at all (regardless of name), then just use a match pattern of
w:pPr[ancestor::w:p[ancestor::w:tc] and not(following-sibling::*)]
or equivalently (and slightly shorter)
w:tc//w:p//w:pPr[not(following-sibling::*)]
Using the XPath is simple and straightforward, you have to filter elements olny. Your filtring could be based on the content of the element (using [] and path inside the brackets). With the filtered elements you can work as same as with the XML tree (start filtering again or select the final elements).
In your case, first you have to choose the correct tc element (filter the element as you need):
Based on the count of elements: //tc[count(./p/*) = 1], or
Based on non existing r element: //tc[not(./p/r)], or
Based on non existing r and hyperlink element: //tc[not(./p/r) and not(./p/hyperlink)]
Based on existing pPr and non existing r (it is not a necessary because the pPr is filtred in second step): //tc[./p/r and not(./p/r)]
It returns the following XML.
<tc>
<p>
<pPr>pPr</pPr>
</p>
</tc>
Then just simply say what do you want from the new XML:
Do you want the pPr element? Use: /p/pPr
All together:
//tc[count(./p/*) = 1]/p/pPr
or
//tc[not(./p/r)]/p/pPr
Note: // means find the element anywhere in the document.
Update 1: Hyperlink condition added.

Does xpath support "or" function

In case below two elements do not show in same time
<a title='a' />
<b title='b' />
I want to check if one of them can show
does xpath support the 'or' function? I just want to write in one line:
//a[#title='a'] or .. #title='b' ??
XPath Operators
Select either matching nodes (your case here):
//a[#title='a'] | //b[#title='b']
Select one element with either matching attributes
//a[#title='a' or #title='b']
If you want to match either <a/> elements with #title='a' attribute or <b/> elements with #title='b' attribute, you can also match all elements and perform a test on their name:
//*[local-name(.) = 'a' and #title='a' or local-name(.) = 'b' and #title='b']

Xpath test for ancestor attribute not equal string

I'm trying to test if an attribute on an ancestor of an element not equal a string.
Here is my XML...
<aaa att="xyz">
<bbb>
<ccc/>
</bbb>
</aaa>
<aaa att="mno">
<bbb>
<ccc/>
</bbb>
</aaa>
If I'm acting on element ccc, I'm trying to test that its grandparent aaa #att doesn't equal "xyz".
I currently have this...
ancestor::aaa[not(contains(#att, 'xyz'))]
Thanks!
Assuming that by saying an ancestor of an element you're referring to an element with child elements, this XPath expression should do:
//*[*/ccc][#att != 'xyz']
It selects
all nodes
that have at least one <ccc> grandchild node
and that have an att attribute whose value is not xyz.
Update: Restricted test to grandparents of <ccc>.
Update 2: Adapted to your revised question:
//ccc[../parent::aaa/#att != 'xyz']
Selects
all <ccc> elements
that have a grandparent <aaa> with its attribute att set to a value that is not xyz

Resources