BaseX XQuery error: root(): no context value bound - xpath

I am trying to run the following XQuery expression in BaseX to extract elements between two succeeding headings. (as an article section).
xquery for $x in doc("test.xq")//h2,
$y in $x/following-sibling::h2[1]
return //*[$x/following::* and $y/preceding::*]
But it gives the error
Error:
Stopped at D:/Program Files/BaseX/data/test.xq, 1/74:
[XPDY0002] root(): no context value bound.
By the expression I mean if $x is heading and $y is the first heading following $x, then select the common text for $x/following::* and $y/preceding::*
However I am not sure my expression works, but my question here is how can execute my intended query without error?
If you have also an expression which works for my need, that is welcomed.

[...] to extract elements between two succeeding headings [...]
You need something more like:
for $x in doc("test.xq")//h2
return $x/following-sibling::*[preceding-sibling::h2[1] is $x]
but on its own it won't give you anything useful because the XPath and XQuery data model only has flat sequences, not "multi-dimensional arrays". When you have a for that returns a sequence of values for each "iteration", the overall result of the for expression is the concatenation of all the result sequences, so as written above this expression will simply return you all the elements in every "section" in a single flat list. If you want to group the elements by section then you'd need to construct a new XML element for each group
for $x in doc("test.xq")//h2
return
<section>{$x/following-sibling::*[preceding-sibling::h2[1] is $x]}</section>

The error (as documented here) comes from this expression:
//*[$x/following::* and $y/preceding::*]
which begins with //. The abbreviation // stands for /descendant-or-self::node()/, which of course begins with /. The XPath standard says:
A / by itself selects the root node of the document containing the
context node. If it is followed by a relative location path, then the
location path selects the set of nodes that would be selected by the
relative location path relative to the root node of the document
containing the context node.
But from what you've shown us, there is nothing indicating that you've established a context node. So XPath doesn't have any way to know what document contains the context node. That's what the error message is referring to when it says
root(): no context value bound
To fix the error, you could precede the // with an explicit doc(...) or any other explicit way to set the context:
doc("test.xq")//*[$x/following::* and $y/preceding::*]
or
root($x)//*[$x/following::* and $y/preceding::*]
This should get rid of the error, but as Ian Roberts has written, it won't give you the result you want. See his answer for that.

Related

Choosing specific element in XPath

I got 2 elements under the same name "reason". When i'm using //*:reason/text() it gives me both of the elements, but i need the first one. (not the one inside "details"). please help..
<xml xmlns:gob="http://osb.yes.co.il/GoblinAudit">
<fault>
<ctx:fault xmlns:ctx="http://www.bea.com/wli/sb/context">
<ctx:errorCode>BEA-382500</ctx:errorCode>
<ctx:reason>OSB Service Callout action received SOAP Fault response</ctx:reason>
<ctx:details>
<ns0:ReceivedFaultDetail xmlns:ns0="http://www.bea.com/wli/sb/stages/transform/config">
<ns0:faultcode xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">soapenv:Server</ns0:faultcode>
<ns0:faultstring>BEA-380001: Internal Server Error</ns0:faultstring>
<ns0:detail>
<con:fault xmlns:con="http://www.bea.com/wli/sb/context">
<con:errorCode>BEA-380001</con:errorCode>
<con:reason>Internal Server Error</con:reason>
<con:location>
<con:node>RouteTo_FinancialControllerBS</con:node>
<con:path>response-pipeline</con:path>
</con:location>
</con:fault>
</ns0:detail>
</ns0:ReceivedFaultDetail>
</ctx:details>
<ctx:location>
<ctx:node>PipelinePairNode2</ctx:node>
<ctx:pipeline>PipelinePairNode2_request</ctx:pipeline>
<ctx:stage>set maintain offer</ctx:stage>
<ctx:path>request-pipeline</ctx:path>
</ctx:location>
</ctx:fault>
</fault>
</xml>
You are using the // qualifier which will descend into any subtree and find all occurences of reason. You can try to be more specific about the subpath:
//fault/*:fault/*:reason/text()
This will only match the outer reason but not the inner reason..
"...but i need the first one"
You can use position index to get the first matched reason element :
(//*:reason)[1]/text()
" (not the one inside "details")"
The above can be expressed as finding reason element which doesn't have ancestor details :
//*:reason[not(ancestor::*:details)]/text()
For a large XML document, using more specific path i.e avoid // at the beginning, would results in a more efficient XPath :
/xml/fault/*:fault/*:reason/text()
But for a small XML, it's just a matter of personal preference, since the improvement is likely to be negligible.

immediately preceding-sibling must contain attribute

Here is my XML file:
<w type="fruit-hard">apple</w>
<w type="fruit-soft">orange</w>
<w type="vegetable">carrot</w>
I need to find carrot's immediately preceding sibling whose type is fruit-soft. In Chrome (locally loaded XML file), when I try
$x("//w[#type='vegetable']/preceding-sibling::w[1]")
I get "orange" element node like I want, but how do I require that its type be "fruit-soft"? My attempt (below) returns "false."
$x("//w[#type='vegetable']/preceding-sibling::w[1] and preceding-sibling::w[#type='fruit-soft']")
Your original XPath ...
//w[#type='vegetable']/preceding-sibling::w[1]
... is equivalent to
//w[#type='vegetable']/preceding-sibling::w[position()=1]
. You can add additional criteria to the predicate as needed:
//w[#type='vegetable']/preceding-sibling::w[position()=1 and #type='fruit-soft']
Or you can add an add a separate predicate
//w[#type='vegetable']/preceding-sibling::w[1][#type='fruit-soft']
Note that this attempt:
//w[#type='vegetable']/preceding-sibling::w[1] and preceding-sibling::w[#type='fruit-soft']
returns false because the parts on either side of the and are evaluated separately, converted to type boolean, and combined to yield the final result. Supposing that the context node against which that is evaluated is the document root, there will never be a node matching preceding-sibling::w[#type='fruit-soft']. Moreover, even if there were such a node, that expression does not require nodes matching the first part to be the same ones that matches the second part.

Can I get the information at which node or element or attribute the xpath failed while evaluating it against an xml

I have some xpath and I am evaluating against an XML.
//view/section/row
[(cell/data[#value='Other Roles']) and
(cell/data[contains(#value,'336')]) and
(cell/data[contains(#value,'0')]) and
(cell/data[contains(#value,'320')]) and
(cell/data[contains(#value,'16')]) and
(cell/data[contains(#value,'0')]) ]
While doing so, the xpath might not be available say row does not have the cell with data 336 , can I get that piece of information where it failed
Any code/utils that gives this information
In general, No.
Even if the result set is empty, it does not mean it fails. It is just an empty result set, which is a valid result. So as a matter of fact, your assumption is wrong, because the XPath did not fail.
If you want to check whether your XPath yiels an empty sequence, you can check using fn:empty(), e.g. empty(cell/data[contains(#value,'336')]).
Using XPath 2.0 you can also raise your own errors, using the fn:error() function. However, I do not see how you want to apply that in this specific example in a useful manner.
I've not seen any tools that automatically do this, but manually performing such sanity checks can be very useful:
First check that you're matching views:
//view
then sections:
//view/section
then rows:
//view/section/row
then specific rows:
//view/section/row[(cell/data[#value='Other Roles'])]
...until you get to a point where reality deviates from your expectations. You'll then know where an adjustment must be made.

using parent dot notation in xpath to find another branch in the XML tree

I'm running an xslt on an XML that at one point puts my current node at the following "item" element in the tree:
host/device/item
I've found through experimentation that the following xpath takes the xslt back up to the "host" branch of the XML tree, then successfully locates the "trigger" grand child element of the "host" element.
../.././setting/trigger
It works, but the xpath syntax seems odd to me. The dot parent notation ../../. makes sense when you read it from the . from right to left. But the child phrase notation setting/trigger only make sense if you read from left to right. The "final meaning" of the entire xpath is equivalent to saying:
host/setting/trigger
Is it always true that the middle ../. section of the xpath (or however many parent ../ levels it is) is always ignored to create the final meaning host/device/trigger?
Since . means self, you could interpret it as being ignored. ./././././././* means the same as ./*, which means the same as *.
Regarding Reading the XPath, breaking it down from the context node host/device/item
In ../.././setting/trigger, the / characters separate the node tests, so, LtoR:
.. you are now at host/device,
.. you are now at host
. you are still at host
setting you are now at host/setting
trigger you are now at host/setting/trigger
If you read it RtoL, then you must understand that instead of following node-tests, you are reading each node test with the opposite meaning, and your context is where you want to end up, so:
from host/setting/trigger (your desired endpoint)
trigger (read as ..), you are now at host/setting
setting (again, ..), you are now at host
. (.), you are still at host
.. (read as *) you might now be at host/device
.. (read as *) you might now be at host/device/item

Can't get nth node in Selenium

I try to write xpath expressions so that my tests won't be broken by small design changes. So instead of the expressions that Selenium IDE generates, I write my own.
Here's an issue:
//input[#name='question'][7]
This expression doesn't work at all. Input nodes named 'question' are spread across the page. They're not siblings.
I've tried using intermediate expression, but it also fails.
(//input[#name='question'])[2]
error = Error: Element (//input[#name='question'])[2] not found
That's why I suppose Seleniun has a wrong implementation of XPath.
According to XPath docs, the position predicate must filter by the position in the nodeset, so it must find the seventh input with the name 'question'. In Selenium this doesn't work. CSS selectors (:nth-of-kind) neither.
I had to write an expression that filters their common parents:
//*[contains(#class, 'question_section')][7]//input[#name='question']
Is this a Selenium specific issue, or I'm reading the specs wrong way? What can I do to make a shorter expression?
Here's an issue:
//input[#name='question'][7]
This expression doesn't work at all.
This is a FAQ.
[] has a higher priority than //.
The above expression selects every input element with #name = 'question', which is the 7th child of its parent -- and aparently the parents of input elements in the document that is not shown don't have so many input children.
Use (note the brackets):
(//input[#name='question'])[7]
This selects the 7th element input in the document that satisfies the conditions in the predicate.
Edit:
People, who know Selenium (Dave Hunt) suggest that the above expression is written in Selenium as:
xpath=(//input[#name='question'])[7]
If you want the 7th input with name attribute with a value of question in the source then try the following:
/descendant::input[#name='question'][7]

Resources