I have an XQuery problem:
{
let $x := $document//AOSCAT_MetricDetail//table[#class="pretty-table"]
//tr/td[8]/text()
let $sum_of_statements_per_method := sum(
for $a at $i in $x where $a != 0.0 return $a
)
let $count_of_statements_per_method := count(
for $a at $i in $x where $a != 0.0 return $a
)
return
if ($count_of_statements_per_method != 0) then
return <average_statements_per_method>{
$sum_of_statements_per_method
div
$count_of_statements_per_method
}</average_statements_per_method>
else
return <average_statements_per_method>
No data available
</average_statements_per_method>
}
Executing this XQuery with Saxon9 yields the following Error:
Error on line 103 column 39 of transform_2.xq:
XPST0003: XQuery syntax error in #...verage_statements_per_method>{#:
expected "else", found ">"
Static error(s) in query
But i dont understand why. In the same File a have a very similar XQuery expression and there's no problem at all...
Is anyone able to point me to where my mistake is ?
EDIT:
But why does this work ok, then? :
{
let $content_check := $document//AOSCAT_MainReport//div[#class="reportpart"][4]/h2/text()
return
if($content_check="Abstraction analysis") then
let $abstraction := $document//AOSCAT_MainReport//div[#class="reportpart"][4]/table//tr[2]//td[5]
return <abstraction_level>{ fn:number(fn:normalize-space($abstraction))*100 }</abstraction_level>
else
let $abstraction := 0
return <abstraction_level>{ $abstraction }</abstraction_level>
}
You have too many returns. Your code:
return
if (...) then ...
return ...
else
return ...
Should be:
return
if (...) then ...
else ...
The XQuery spec defines an if-statement as:
IfExpr ::= "if" "(" Expr ")" "then" ExprSingle "else" ExprSingle
ExprSingle can be a value or another expression. Your second example is the latter case: the let following the then branch implies it is a FLWOR-type expression, and therefore must include a return. Simply: using for or let requires return.
For more information on the XQuery grammar, please see the spec or the many resources online that explain it more simply.
Related
Im trying to implement base64 coding in a very simple way. In my approach (lets for a second put away whether its appropriate or not) I need to reverse strings and then concate them. After that this concated string is used in substring function. Strings are joined properly but when I use substring basex seems to lose it.
Funny thing is substring works for well for all indexes starting at 8. So substring($string, 1, 8) and higher gives correct output. But everything below that is messed up. Starting with one disappeared number: substring($string, 1, 7 (and below) ) results in 6 length string.
Moreover substring can start only with 1st or 0 index. Anything greater results in empty return.
declare variable $array := [];
declare function bs:encode
( $input as xs:string ) {
bs:integer-to-binary(string-to-codepoints($input), "", $array)
} ;
declare function bs:integer-to-binary
( $input as xs:integer*, $string as xs:string, $array as array(xs:string) ) {
let $strings :=
for $i in $input
return
if ($i != 0)
then if ($i mod 2 = 0)
then bs:integer-to-binary(xs:integer($i div 2), concat($string, 0), $array)
else bs:integer-to-binary(xs:integer($i div 2), concat($string, 1), $array)
else if ($i <= 0)
then array:append($array, $string)
return bs:check-if-eight($strings)
} ;
declare function bs:check-if-eight
( $strings as item()+ ) {
let $fullBinary :=
for $string in $strings
return if (string-length($string) < 8)
then bs:check-if-eight(concat($string, 0))
else $string (: add as private below :)
return bs:concat-strings($fullBinary)
} ;
declare function bs:concat-strings
( $strings as item()+ ) {
let $firstStringToConcat := functx:reverse-string($strings[position() = 1])
let $secondStringToConcat := functx:reverse-string($strings[position() = 2])
let $thirdStringToConcat := functx:reverse-string($strings[position() = 3])
let $concat :=
concat
($firstStringToConcat,
$secondStringToConcat,
$thirdStringToConcat)
(: this returns correct string of binary value for Cat word :)
return bs:divide-into-six($concat)
} ;
declare function bs:divide-into-six
( $binaryString as xs:string) {
let $sixBitString := substring($binaryString, 1, 6)
(: this should return 010000 instead i get 000100 which is not even in $binaryString at all :)
return $sixBitString
} ;
bs:encode("Cat")
I expect first six letters from string (010000) instead I get some random sequence I guess (00100). The whole module is meant to encode strings into base64 format but for now (the part i uploaded) should just throw first six bits for 'C'
Alright so I figured it out I guess.
First of all in function concat-strings I changed concat to fn:string-join. It allowed me to pass as an argument symbol that separates joined strings.
declare function bs:concat-strings ( $strings as item()+ ) {
let $firstStringToConcat := xs:string(functx:reverse-string($strings[position() = 1]))
let $secondStringToConcat := xs:string(functx:reverse-string($strings[position() = 2]))
let $thirdStringToConcat := xs:string(functx:reverse-string($strings[position() = 3]))
let $concat :=
****fn:string-join(****
($firstStringToConcat,
$secondStringToConcat,
$thirdStringToConcat),****'X'****)
return bs:divide-into-six($concat) } ;
I saw that my input looked like this:
XXXXXXXX01000011XXXXXXXXXXXXXXXXX01100001XXXXXXXXXXXXXXXXX01110100XXXXXXXX
Obviously it had to looping somewhere without clear for loop and as I novice to Xquery i must have been missed that. And indeed. I found it in check-if-eight function:
> declare function bs:check-if-eight ( $strings as item()+ ) {
> **let $fullBinary :=**
> for $string in $strings
> return if (string-length($string) < 8)
> then bs:check-if-eight(concat($string, 0))
> else $string (: add as private below :)
> **return bs:concat-strings($fullBinary)** } ;
Despite being above FOR keyword, $fullBinary variable was in a loop and produced empty spaces(?) and it was clearly shown when i used X as a separator.
DISCLAIMER: I thought about this before and used functx:trim but for some reason it doesnt work like I expected. So it might not for you too if having similar issue.
At this point it was clear that let $fullBinary cannot be bided in FLWR statement at least can't trigger concat-strings function. I changed it and now it produces only string and now im trying to figure out new sequence of running whole module but I think the main problem here is solved.
I have to do something like:
declare function xf:NameOfXQ($myIn as element(ns2:myIn))
as element() {
<ns0:tagIn>
<myObj>
{
for $var in $myValueOfVar
return
if ($var= "X") then
<tagA>A</tagA>
<tagB>B</tagB>
<tagC>C</tagC>
else
<tagD>D</tagD>
}
</myObj>
</ns0:tagIn>
};
But when the xquery is build, my project says "Invalid expression, expecting ELSE, found '>'"
I try then use 4 IF's, one to fill each tag, like:
declare function xf:NameOfXQ($myIn as element(ns2:myIn))
as element() {
<ns0:tagIn>
<myObj>
{
for $var in $myValueOfVar
return
if ($var= "X") then
<tagA>A</tagA>
else
()
if ($var= "X") then
<tagB>B</tagB>
else
()
if ($var= "X") then
<tagC>C</tagC>
else
()
if ($var != "X") then
<tagD>D</tagD>
else
()
}
</myObj>
</ns0:tagIn>
};
Then my project says "Invalid expression: unexpected token: if"
How I can do it correctly?
XQuery doesn't consider multiple consecutive tags as syntactically correct, an XML fragment must have a root tag to be valid.
It however allows you to use sequence types with the syntax (item1, item2, ... itemN) which you can use to let the return clause of your FLWOR expression yield multiple XML tags :
for $var in $myValueOfVar
return
if ($var= "X") then
(
<tagA>A</tagA>,
<tagB>B</tagB>,
<tagC>C</tagC>
)
else
<tagD>D</tagD>
I am passing a parameter ('articles-counter') from a form to a function in an eXist-db app.
If there is nothing passed (the field is not filled in), this works:
let $xmldb-food :=
(
let $articles-counter :=
(
let $counter-passed := request:get-parameter('articles-counter', '')
return
if ($counter-passed) then
$counter-passed
else ()
)
for $uri at $count in (1 to xs:integer($articles-counter))
let $param := request:get-parameter(concat('article-uri-', $count), '')
return
if ($param ne '') then
string-join($param, ',')
else ()
)
The next example does not. It throws xs:integer is expected (in the for range) but got ''. What is going on here? I would think it is almost the same:
let $xmldb-food :=
(
let $articles-counter := request:get-parameter('articles-counter', '')
for $uri at $count in (1 to xs:integer($articles-counter))
let $param := request:get-parameter(concat('article-uri-', $count), '')
return
if ($param ne '') then
string-join($param, ',')
else ()
)
What is the difference between '' and () (empty sequence)?
Invoking the xs:integer() constructor with an empty string will throw a (dynamic runtime) error. Invoking with an empty sequence returns an empty sequence.
You could filter out the empty string (and any other invalid integer values) with a predicate to avoid the dynamic error condition:
for $uri at $count in (1 to xs:integer($articles-counter[. castable as xs:integer]))
I am trying to get all the values of json string as a single string. for example
in xquery xml
let $x := <a> welcome to the world of <b> JSONiq </b></a>
return string($x)
will return welcome to the world of JSONiq
what is the equvalent result of of following documents in JSONiq:
let $y := {"a":"welcome to the world of ","b":" JSONiq"}
return xxxx($y)
The result should be same welcome to the world of JSONiq
If you know in javascript also it would be great.
First you need to get all values, either with libjn:values or its definition, then you can use fn:string-join to get a single string:
So
declare namespace libjn = "http://jsoniq.org/function-library";
let $y := {"a":"welcome to the world of ","b":" JSONiq"}
return string-join(libjn:values($y) ! string(), "")
or
let $y := {"a":"welcome to the world of ","b":" JSONiq"}
return string-join($y() ! string($y(.)), "")
This might also return "JSONiqwelcome to the world of ", since the object keys are unordered
Find the recursive JSONiq script here for trying out.
Just doing typeswitch and recursive calls does it:
declare function local:strings($v as item()) as xs:string*
{
typeswitch ($v)
case $object as object() return
for $k in jn:keys($object)
return local:strings($object($k))
case $array as array() return
for $member in jn:members($array)
return local:strings($member)
default return $v
};
let $y := {"a":"welcome to the world of", "b":" JSONiq", "x":{"d":"m"}}
return string-join(local:strings($y), "#")
The "#" is only for showing where the borders are, can be replaced by "":
welcome to the world of#JSONiq#m
Hermann.
is there a way to assign a numeric operator to a variable in Xquery?
I have to perform an arithmetic expression on a given pair of values depending upon a node tag.
I've managed to do this but its resulted in a lot of duplicate code. I'd like to simplify the query so that instead of:
Function for Add
Repeated if code - this calls out to other functions but is still repeated
$value1 + $value2
Function for Minus
Repeated if code
$value1 - $value2
etc for multiply, div etc
I'd like to set up a function and send a variable to it, something similar to this:
$value1 $operator $value2
Is there a simple way to do this in xquery?
thank you for your help.
If your query processor supports XQuery 3.0, you can use function items for that:
declare function local:foo($operator, $x, $y) {
let $result := $operator($x, $y)
return 2 * $result
};
local:foo(...) can then be called like this:
let $plus := function($a, $b) { $a + $b },
$mult := function($a, $b) { $a * $b }
return (
local:foo($plus, 1, 2),
local:foo($mult, 3, 4)
)
Why don't you use a simple if-else construct? E.g.
if (repeated code says you should add) then
$value1 + $value2
else
$value1 - $value2
You could also simple put the repeated code in another function instead of copying the code.