XPath predicate combinations for multiple elements - xpath

Using ruby 1.9.3 and Nokogiri (latest):
Given (no, I did not come up with this):
<root>
<subelement>
<key>
<var name="id">50</var>
<var name="secondaryid">0</var>
</key>
</subelement>
<subelement>
<key>
<var name="id">50</var>
<var name="secondaryid">1</var>
</key>
</subelement>
</root>
Return the parent element (<key>) which has a var element with name property equal to "id" and a value equal to 50 AND a var element with name property equal to "secondaryid" and a value equal to 0. Do not return the node with id=50 and secondaryid=1.
Obviously it's going to be built off something along the lines of:
#doc.xpath("//var[#name='id' and text()=50]")
but I can't figure out how to add another predicate that will match the name = "secondaryid" element too.

Not tested with Ruby, but this should do the trick.
//key[var[#name='id'] = '50'][var[#name='secondaryid'] = '0']

Another approach:
subelement/key
[var[#name="id" and . = "50"]]
[var[#name="secondaryid" and . = "0"]]

Related

Simplify specific XPath expression

I would like to know if the following XPath expression can be simplified:
//map[requester/#type='2' and requester/code]
Some test data:
<root>
<map>
<requester type="2">
<code>a</code>
<code>b</code>
</requester>
</map>
...
</root>
My objective is to get only map elements which have at least one requester with type attribute and value '2' and also have at least one code element.
For your use case, this is probably as simple as it could be. However, it doesn't match what you are describing doing.
Here you are selecting map elements where
There is a requester element with type attribute equal to 2
There is a requester element with a code element
The requester elements in (1) and (2) are not necessarily the same
For example, the map element in the following is selected:
<root>
<map>
<requester type="2"/>
<requester>
<code>a</code>
</requester>
</map>
</root>
If you want the elements in (1) and (2) to be the same, you should use (simplified slightly at the suggestion of kjhughes)
//map[requester[#type='2']/code]
Here we select all map elements which have a requester element which in turn has an attribute type with a value of 2 and a code element.

Self axis in xslt

<element>
<bye>do not delete me</bye>
<hello>do not delete me</hello>
<hello>delete me</hello>
<hello>delete me</hello>
</element>
Applied to the above xml, this deletes all the nodes except the first hello child of /element:
<xsl:template match="hello[not(current() = parent::element/hello[1])]" />
Why these ones doesn't work? (assuming the first node is not a text node)
<xsl:template match="hello[not(self::hello/position() = 1)]" />
<xsl:template match="hello[not(./position() = 1)]" />
Or this one?
<xsl:template match="hello[not(self::hello[1])]" />
What is the self axis selecting? Why isn't this last example equivalent to not(hello[1])?
First, you are wrong when you say that:
This deletes all the nodes except the first hello child of /element
The truth is that it deletes (if that's the correct word) any hello child of /element whose value is not the same as the value of the first one of these. For example, given:
XML
<element>
<hello>a</hello>
<hello>b</hello>
<hello>c</hello>
<hello>a</hello>
</element>
the template:
<xsl:template match="hello[not(current() = parent::element/hello[1])]" />
will match the second and the third hello nodes - but not the first or the fourth.
Now, with regard to your question: in XSLT 1.0, position() is not a valid location step - so this:
<xsl:template match="hello[not(self::hello/position() = 1)]" />
should return an error.
In XSLT 2.0, the pattern hello[not(self::hello/position() = 1)] will not match any hello element - because there is only one node on the self axis, and therefore its position is always 1.
Similarly:
<xsl:template match="hello[not(./position() = 1)]" />
is invalid in XSLT 1.0.
In XSLT 2.0, ./position() will always return 1 for the same reason as before: . is short for self::node() and there is only one such node.
Finally, this template:
<xsl:template match="hello[not(self::hello[1])]" />
is looking for a node that doesn't have (the first instance of) itself. Of course, no such node can exist.
Using position() on the RHS of the "/" operator is never useful -- and in XSLT 1.0, which is the tag on your question, it's not actually permitted.
In XSLT 2.0, the result of the expression X/position() is a sequence of integers 1..count(X). If the LHS is a singleton, like self::E, then count(X) is one so the result is a single integer 1.

reading value actual from xml path

I have the following xml structure:
<?xml version="1.0" encoding="UTF-8"? >
<sql>
<Assoc name="sql">
<RecArray name="contents">
<Record name="contents">
<String name="PackType" > < value actual="P" />< /String >
<String name="SerialNumber" > < value actual="0002" />< /String >
<String name="VersionNumber" > < value actual="02" /></ String >
</Record>
</RecArray>
</Assoc>
</sql>
how can i get the values of each of the String nodes like i need to know the value inside the node of "SerialNumber"
Regards,
If you wan to get all <value> elements inside each <String> element, you can try this XPath query :
/sql/Assoc/RecArray/Record/String/value
precise path will be better performance wise. If you're looking for simpler query, this will also work :
//String/value
or if you mean by values of each of the String nodes is value of actual attribute, you can do this way :
/sql/Assoc/RecArray/Record/String/value/#actual
Finally, if none of above meet your requirement, please update the question and provide expected output from sample XML posted.
i figured it out
as it is multi String elements (that was clear in the question), i should use the following
/sql/Assoc/RecArray/Record/String[2]/value/#actual

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.

Get the non-empty element using XPATH

I have the following XML
<?xml version = "1.0" encoding = "UTF-8"?>
<root>
<group>
<p1></p1>
</group>
<group>
<p1>value1</p1>
</group>
<group>
<p1></p1>
</group>
</root>
is it possible to get the last the node with value? in this case get the value of the second group/p1.
This xpath should work as well:
//group/p1[string-length(text()) > 0]
How about something like /root/group/p1[text() and not(../following-sibling::group/p1/text())]
In other words: get the p1 elements that have text and whose group parents are not followed by group nodes that have non-empty p1 elements.
You may also use [not(node())] Selector.
Example: //group/p1[not(node())]
It actually can be simplified as below:
//group/p1[string-length() > 0] => element text is non-empty
//group/p1[string-length() = 6] => element text has length 6

Resources