XPath operator "!=". How does it work? - xpath

XML document:
<doc>
<A>
<Node>Hello!</Node>
</A>
<B>
<Node/>
</B>
<C>
</C>
<D/>
</doc>
How would you evaluate the following XPath queries?
/doc/A/Node != 'abcd'
/doc/B/Node != 'abcd'
/doc/C/Node != 'abcd'
/doc/D/Node != 'abcd'
I would expect ALL of these to evaluate to true.
However, here are the results:
/doc/A/Node != 'abcd' true
/doc/B/Node != 'abcd' true
/doc/C/Node != 'abcd' false
/doc/D/Node != 'abcd' false
Is this expected behavior? Or is it a bug with my XPath provider (jaxen)?

Recommendation: Never use the != operator to compare inequality where one or both arguments are node-sets.
By definition the expression:
$node-set != $value
evaluates to true() exactly when there is at least one node in $node-set such that its string value is not equal to the string value of $value.
Using this definition:
$empty-nodeset != $value
is always false(), because there isn't even a single node in $empty-nodeset for which the inequality holds.
Solution:
Use:
not($node-set = $value)
Then you get all results true(), as wanted.

From the XPath spec:
If one object to be compared is a node-set and the other is a string, then the comparison will be true if and only if there is a node in the node-set such that the result of performing the comparison on the string-value of the node and the other string is true.
This means that if the node-set is empty (as in your cases C and D), the result of the boolean expression will be false, since there is no node to which the inequality can apply.
You can work around this behaviour and get the result you want using an expression like:
count(/doc/C/Node) = 0 or /doc/C/Node != 'abcd'

Related

Comparing to a null string in PL/SQL

I am looking for a way to compare a string to a null string the optimal way.
If I do
IF(str1 <> str2) THEN
dbms_output.put_line('Strings are not equal');
END IF;
This gets ignored if str1 is NULL.
I know I can set a null string to something before comparing to make it work
str1 := NVL(str1, 'Empty');
IF(str1 <> str2) THEN
dbms_output.put_line('Strings are not equal');
END IF;
And that works but I wanted to see if there is a better way to handle null string comparisons
You didn't say whether the string you must compare (str1) may be null; nor what the result of the comparison should be if either string is null.
It very rarely (if ever) makes sense to consider a non-null string equal to null. Sometimes it does make sense to consider two null strings as equal (if you think of them as "empty strings" - idiotically, Oracle doesn't make the distinction, mandated by the SQL Standard, between the SQL concept of null, on the one hand, and the empty string concept on the other hand).
The condition below:
str1 = str2
returns TRUE if both strings are non-null and they are equal; it returns FALSE if both strings are non-null and they are different; and it returns UNKNOWN if at least one string is null.
The LNNVL operator (which takes a condition as its argument) returns FALSE when its argument is TRUE and it returns TRUE when its argument is either FALSE or UNKNOWN. So, the condition
lnnvl(str1 = str2)
returns FALSE when the strings are both non-null and equal, and TRUE in all other cases. If you must consider two null strings as "not equal", then this is the condition you need in your code.
If, on the other hand, you must view two null strings as equal, you can use
decode(str1, str2, 0) is not null
In the case above, DECODE returns 0 if str1 and str2 are both non-null and equal, and also when they are both null. (This is why DECODE is often used in exactly this kind of situation). It returns the default (which I didn't specify, so the function will return the "default" default value, which is NULL) in the remaining cases. DECODE will return non-NULL when str1 and str2 are "the same" - either as equal, non-null strings, or as both null.
Of course, the DECODE condition can also be written as
decode(str1, str2, 0) = 0
or even
decode(str1, str2, 0, 1) = 0
to make it more explicit. Some people (not me!) may prefer it that way.
What if str2 literally is 'Empty'? nvl(str1, 'Empty'); (which is what you mean by DECODE(str1, 'Empty') I assume (decode() needs at least three parameters so your original expression would fail)) will give you a false negative. Just use IS NULL.
IF str1 <> str2
OR str1 IS NULL
OR str2 IS NULL THEN
dbms_output.put_line('Strings are not equal');
END IF;
If either str1 or str2 can be NULL then you can use:
IF str1 <> str2
OR str1 IS NULL AND str2 IS NOT NULL
OR str1 IS NOT NULL AND str2 IS NULL
THEN
dbms_output.put_line('Strings are not equal');
END IF;
db<>fiddle here

Unable to Match numbers only using Regular Expression in golang using Regex

matched, err := regexp.MatchString(`[0-9]`, `a.31`)
fmt.Println(matched)
The above expression is returning true.. Isn't it supposed to be false?
I want to extract only numbers but why is "a.31" considered true? I've noticed that having atleast a number in the string will return "true"...
How to make it return "true" only for numbers?

XPATH to select all not null childs plus a default value when not found

Is there an XPATH to return all non-null someChild plus a default value for when the value on is not found?
<someFather>
<someChild/>
<someChild/>
<someChild>some value</someChild>
<someChild/>
<someChild>some other value</someChild>
<someChild/>
</someFather>
I would like to get:
""
""
some value
""
some other value
""
, or
"not-found"
"not-found"
some value
"not-found"
some other value
"not-found"
/someFather/someChild/(text()/string(), "not-found")[1]
This is carefully written to avoid breaking the rule that the RHS of "/" cannot select a mixture of nodes and atomic values. In 3.0 you could use the "!" operator:
/someFather/someChild ! (text(), "not-found")[1]
Try the following expression:
/someFather/(someChild/string(), '')

XPath query that matches multiple attributes of any particular element name

Given the following sample XML:
<a z="123" y="321"></a>
<b z="456" y="654"></b>
<c x="456" w="654"></c>
<c x="123" w="111"></c>
<c x="789" w="321"></c>
I need an xpath query that will return element 'a', because there is a 'c' element whose #x equals the a's #z, and whose #w does NOT equal the a's #y.
Notice that 'b' is not returned because there is a 'c' element where #x=#z and #w=#y.
Also, the elements being returned can be any element (*). The important bit is there is a matching 'c' element, where the second attribute doesn't match.
The closest I've come up with is this:
//*[#z=//c/#x and .[#y != //c/#w]]
However in my sample above, this would not return 'a' because #z matches #x of a 'c' element, and #y matches #w of a different 'c' element. The second attribute check needs to be made against the same 'c' element.
I hope this makes sense.
This XPath 2.0 expression:
//*[
let $a := .
return
following-sibling::*[#x eq $a/#z and not(#w eq $a/#y)]
]
Will bind the matched element to a variable in a predicate, and then use it in a predicate for the following-sibling elements of that context element to see if their attributes satisfy the stated requirements.

NULL literal in XPath

Is there a NULL literal in XPath 1.0 or 2.0?
My use case is that I have a conditional (if then else) XPath expression and I want to return NULL to signify a certain condition. I am afraid that returning an empty string might be ambiguous in my case as it could be a valid result of the other part of the if then else expression.
The empty sequence () can be used as such. It is also returned if there is no result for a path expression.
let $foo := "foo"
return
if ($foo = ("foo", "bar", "batz")) then
$foo
else
()
You can check for an empty sequence using
let $result := ()
return empty($result)
If you pass the result of the first XPath expression to your native code, you should be able to distinguish "NULL" from the empty string by having no results (empty sequence / "NULL") or having a result string (which could be empty).

Resources