XPath query to match depending on combinations of child elements - xpath

Given the following XML Snippet
<Events>
<Event>
<DateTime>22.09.2009 11:27:18</DateTime>
<EventType>Download</EventType>
</Event>
What is the XPath query to return all Events created today of type download?

/Events/Event[starts-with(DateTime, '22.09.2009') and EventType='Download']
Since I assume that this is a follow-up to your previous question, you might want to use this snippet instead of SelectSingleNode to get all events in a file (if there can be multiple):
foreach (XPathNavigator node in doc.CreateNavigator().Select(expression)) {
// matching node found in document; will process all matching nodes
}

//Events/Event[contains(DateTime,'22.09.2009') and EventType='Download']

/Events/Event[substring(DateTime, 0, 10)='22.09.2009' and EventType='Download']

Related

xpath expression to read value based on value of sibling

I've below xml and would like to read the value of 'Value' tag whose Name matches 'test2'. I'm using the below xpath , but did not work. Can someone help.
/*[ local-name()='OutputData']/*[ local-name()='OutputDataItem']/*[ local-name()='Name'][normalize-space(.) = 'test2']//*[local-name()='Value']/text()
<get:OutputData>
<get:OutputDataItem>
<get:Name>test1</get:Name>
<get:Value/>
</get:OutputDataItem>
<get:OutputDataItem>
<get:Name>test2</get:Name>
<get:Value>B5B4</get:Value>
</get:OutputDataItem>
<get:OutputDataItem>
<get:Name>test3</get:Name>
<get:Value/>
</get:OutputDataItem>
<get:OutputDataItem>
<get:Name>OP_VCscEncrptCd_VAR</get:Name>
<get:Value/>
</get:OutputDataItem>
</get:OutputData>
Thanks
You were close, but because the get:name and get:value are siblings, you need to adjust your XPath a little.
Your XPath was attempting to address get:value elements that were descendants of get:name, rather than as siblings. Move the criteria that is filtering the get:name into a predicate, then step down into the get:value:
/*[ local-name()='OutputData']/*[ local-name()='OutputDataItem']
[*[ local-name()='Name'][normalize-space(.) = 'test2']]/*[local-name()='Value']/text()
You could also combine the criteria of the predicate filter on the get:name and use an and:
/*[ local-name()='OutputData']/*[ local-name()='OutputDataItem']
[*[ local-name()='Name' and normalize-space(.) = 'test2']]/*[local-name()='Value']/text()
This should work I think:
//*[local-name()="get:Name" and text()="test2"]/following-sibling::*[local-name()="get:Value"]/text()

Xpath get multiple node-names

i am using xpath to get some node names from a xhtml / xml file.
I currently have this xpath:
/xhtml:html/xhtml:head/xforms:model/xforms:instance/form/*[starts-with(local-name(), 'section')]
That will get the nodes with a name like this:
section-1_s1_partners
section-2-s2_strategy
The result of the above xpath are the matched nodes, but i want to get for each match the full-node-name. When i use the name() function like
name(/xhtml:html/xhtml:head/xforms:model/xforms:instance/form/*[starts-with(local-name(), 'section')])
Then it only returns the first match, and i have no clue how to do it otherwise..
Any great ideas??
Thanks!
(the xhtml/xml: )
<xhtml:html ....>
<xhtml:head>
<xhtml:title>ASD-1</xhtml:title>
<xforms:model id="fr-form-model">
<xforms:instance id="fr-form-instance">
<form>
<section-1_s1_partners>
<control-304/>
<toggleForm>ASD</toggleForm>
<applicationid/>
<section-345>
<s1_kbPaAAr/>
<s1_kbDCCent/>
<s1_kbRAE/>
</section-345>
<section-s1_depDDFentGFress>
<address_search/>
<address_postcode/>
<address_address1/>
<address_address2/>
<address_address3/>
<address_city/>
</section-s1_departmentAddress>
<section-344>
<s1_companyPartner/>
<s1_companyRegistrationNumber/>
<s1_companyType/>
<s1_companySize/>
</section-344>
<section-s1_companyAddress>
<address_search/>
<address_postcode/>
<address_address1/>
<address_address2/>
<address_address3/>
<address_city/>
</section-s1_companyAddress>
<section-324>
<s1_plannedDate/>
<s1_workDescription/>
<s1_publicDescription/>
<s1_numberOfAssociates>1</s1_numberOfAssociates>
<s1_duration/>
<s1b_resubmissionYesNo/>
<s1_GAAGrogramNumber/>
</section-324>
</section-1_s1_partners>
<section-2-s2_strategy>
<control-4/>
<s2_memo_strategic/>
<s2_memo_problems/>
<s2_companyPosition/>
<s2_companyContribution/>
<s2_lackExpertise/>
<s2_essential/>
<s2_companySponsor/>
<s2_seekKnowledge/>
<s2_challenge/>
</section-2-s2_strategy>
The name function need one argument, it cannot take a node list. You have to iterate over the nodelist in the language you are using. For example, in xsh:
for /xhtml:html/xhtml:head/xforms:model/xforms:instance/form/*[starts-with(local-name(), 'section')]
echo name()

Does xpath query has Limit option like mysql

I want to limit number of result I receive from xpath query.
For example:-
$info = $xml->xpath("//*[firstname='Sheila'] **LIMIT 0,100**");
You see that LIMIT 0,100.
You should be able to use "//*[firstname='Sheila' and position() <= 100]"
Edit:
Given the following XML:
<root>
<country.php desc="country.php" language="fr|pt|en|in" editable="Yes">
<en/>
<in>
<cityList desc="cityList" language="in" editable="Yes" type="Array" index="No">
<element0>Abu</element0>
<element1>Agartala</element1>
<element2>Agra</element2>
<element3>Ahmedabad</element3>
<element4> Ahmednagar</element4>
<element5>Aizwal</element5>
<element150>abcd</element150>
</cityList>
</in>
</country.php>
</root>
You can use the following XPath to get the first three cities:
//cityList/*[position()<=3]
Results:
Node element0 Abu
Node element1 Agartala
Node element2 Agra
If you want to limit this to nodes that start with element:
//cityList/*[substring(name(), 1, 7) = 'element' and position()<=3]
Note that this latter example works because you're selecting all the child nodes of cityList, so in this case Position() works to limit the results as expected. If there was a mix of other node names under the cityList node, you'd get undesirable results.
For example, changing the XML as follows:
<root>
<country.php desc="country.php" language="fr|pt|en|in" editable="Yes">
<en/>
<in>
<cityList desc="cityList" language="in" editable="Yes" type="Array" index="No">
<element0>Abu</element0>
<dog>Agartala</dog>
<cat>Agra</cat>
<element3>Ahmedabad</element3>
<element4> Ahmednagar</element4>
<element5>Aizwal</element5>
<element150>abcd</element150>
</cityList>
</in>
</country.php>
</root>
and using the above XPath expression, we now get
Node element0 Abu
Note that we're losing the second and third results, because the position() function is evaluating at a higher order of precedence - the same as requesting "give me the first three nodes, now out of those give me all the nodes that start with 'element'".
Ran into the same issue myself and had some issue with Geoffs answer as it, as he clearly describes, limits the number of elements returned before it performs the other parts of the query due to precedence.
My solution is to add the position() < 10 as an additional conditional after my other conditions have been applied e.g.:
//ElementsIWant[./ChildElementToFilterOn='ValueToSearchFor'][position() <= 10]/.
Notice that I'm using two separate conditional blocks.
This will first filter out elements that live up to my condition and secondly only take 10 of those.

How to get H1,H2,H3,... using a single xpath expression

How can I get H1,H2,H3 contents in one single xpath expression?
I know I could do this.
//html/body/h1/text()
//html/body/h2/text()
//html/body/h3/text()
and so on.
Use:
/html/body/*[self::h1 or self::h2 or self::h3]/text()
The following expression is incorrect:
//html/body/*[local-name() = "h1"
or local-name() = "h2"
or local-name() = "h3"]/text()
because it may select text nodes that are children of unwanted:h1, different:h2, someWeirdNamespace:h3.
Another recommendation: Always avoid using // when the structure of the XML document is statically known. Using // most often results in significant inefficiencies because it causes the complete document (sub)tree roted in the context node to be traversed.

JDOM-XPath: Can't get the second value in a collection

I'm pretty confused about this one. Given the following xml:
<sch:eventList>
<sch:event>
<sch:eventName>Event One</sch:eventName>
<sch:locationName>Location One</sch:locationName>
</sch:event>
<sch:event>
<sch:eventName>Event Two</sch:eventName>
<sch:locationName>Location Two</sch:locationName>
</sch:event>
</sch:eventList>
When using JDOM using the following code:
XPath eventNameExpression = XPath.newInstance("//sch:eventName");
XPath eventLocationExpression = XPath.newInstance("//sch:eventLocation");
XPath eventExpression = XPath.newInstance("//sch:event");
List<Element> elements = eventExpression.selectNodes(requestElement);
for(Element e: elements) {
System.out.println(eventNameExpression.valueOf(e));
System.out.println(eventLocationExpression.valueOf(e));
}
The console shows this:
Event One
Location One
Event One
Location One
What am I missing?
Don't use '//' it starts always searching at the root node. Use e.g. './sch:eventName' it is relative to the current node.

Resources