Xpath to check multiple empty children - xpath

With the following xml, I'm trying to return true or false depending on whether all <CrucialNumber> elements are empty or not:
<Invoice>
<Details>
<LIN1>
<Quantity>1</Quantity>
<Product>Test XML</Product>
<CrucialNumber/>
</LIN1>
<LIN1>
<Quantity>1</Quantity>
<Product>Test XML</Product>
<CrucialNumber/>
</LIN1>
<LIN1>
<Quantity>1</Quantity>
<Product>Test XML</Product>
<CrucialNumber>123456</CrucialNumber>
</LIN1>
</Details>
</Invoice>
The data-type of the CrucialNumber element is string.
So far, these are the xpath expressions I have tried:
string-length(//CrucialNumber/*) > 0;
not(//CrucialNumber/*[text()]);
./Details/LIN1[*]/CrucialNumber[1] = "";

not(.//CrucialNumber/node())
should do it, it will be true if and only if either (a) there are no CrucialNumber descendant elements at all, or (b) all the descendant CrucialNumber elements are empty.

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.

Determine if any element with a given name has a particular value

Given this XML fragment (I've removed superfluous fluff):
<Event name="DataComplete">
<Task id="d20a0053-7678-43ba-bc8a-ece24dcff15b"/>
<DataItems>
<DataItem name="Survey" type="task">
<Value status="NotStarted" taskId="00000000-0000-0000-0000-000000000000" />
</DataItem>
<GroupDataItem name="CT_Visit"> --- this may repeat
<ItemGroup id="1" >
<DataItem name="Special Contractor" type="string">Yes</DataItem>
What xPath expression will determine if any DataItem with name="Special Contractor" has the value "Yes".
I'm trying something like this:
Yes = /Event/Task/DataItems/GroupDataItem/ItemGroup/DataItem/#[normalize-space() = 'Special Contractor']
and many variations usually resulting in "invalid xPath expression".
Any clues most welcome. Thanks!
[EDIT]
Thanks for the answers Jiri and Will. Will was close, but as my question states, I'm trying to determine if any* element has the value Yes. I should have been more explicit in saying that I need a boolean, true or false. Adapting Will's answer led me to this:
"Yes" = //Event/Task/DataItems/GroupDataItem/ItemGroup/DataItem[#name='Special Contractor']
This returns a simple Boolean='true' or Boolean='false'.
Thanks guys!
/Event/DataItems/GroupDataItem/ItemGroup/DataItem[#name = "Special Contractor"][. = "Yes"]
Returns the DataItem in question. Note that this will be a sequence of matching DataItem elements if there are more than one. If you just want a boolean:
exists(/Event/DataItems/GroupDataItem/ItemGroup/DataItem[#name = "Special Contractor"][. = "Yes"])
(as an aside; I removed Task from the xpath, since it's not actually an ancestor of the DataItem in the XML fragment you posted, even though the indentation makes it look like it is.)
Use this xpath
/Event/Task/DataItems/GroupDataItem/ItemGroup/DataItem[#name='Special Contractor']
for following xml:
<Event name="DataComplete">
<Task id="d20a0053-7678-43ba-bc8a-ece24dcff15b">
<DataItems>
<DataItem name="Survey" type="task">
<Value status="NotStarted" taskId="00000000-0000-0000-0000-000000000000" />
</DataItem>
<GroupDataItem name="CT_Visit"> --- this may repeat
<ItemGroup id="1" >
<DataItem name="Special Contractor" type="string">Yes</DataItem>
</ItemGroup>
</GroupDataItem>
</DataItems>
</Task>
...
</Event>
If the task is really non-pair element, then omit it from the xpath expression.

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

XPath Get first element of subset

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.

XPath query to match depending on combinations of child elements

I have the following Xml structure; Payment/Line which has amongst its element a IsFeePayment and a IsServiceProduct elements of type bool.
<Payment>
<Line>
<IsFeePayment>true</IsFeePayment>
<ISServiceProduct>true</IsServiceProduct>
</Line>
</Payment>
i want an xpath statement that returns 'true' when both of these are are they are, true.
if either one is false, i want the xpath statement to return 'false'
THe xpath below is almost there, it returns the line when both are true.
/[local-name()='Payment']/[local-name()='Line'][*[local-name()='IsFeePayment'][text()='true'] and *[local-name()='IsServiceProduct'][text()='true']]
how do i just get a simple bool out instead of the whole element?
You can simplify the xpath to
boolean(//Payment/Line[IsFeePayment='true' and IsServiceProduct='true'])
simply adding a boolean() around the xpath expression i already had fixes the problem blush
so ...
boolean(/[local-name()='Payment']/[local-name()='Line'][*[local-name()='IsFeePayment'][text()='true'] and *[local-name()='IsServiceProduct'][text()='true']])

Resources