Cast exception on recursion+map
Wednesday, 29 October 2014 · 4 messages · last reply 30 Oct 2014
ML
Marco Lettere29 Oct 2014 10:49 #
Hello all,
I've stumbled upon an issue with the latest version and in particular the code I provide was working with 8.0 snapshot up to build 496C381 and with all previous official versions of course. Build 151df63 is the first build I'm aware of the difference.
Shortly. In an input document I have a list of env nodes (sort of <env name="n">v</env>). They need to be parsed and "
xquery:eval(uated)" in sequence because following env nodes may refer to preceding ones (i.e. <env name="n1">v1</env>, <env name="n2">{$n1}</env>).
I've managed to do the job with the following code but now (as described in the preamble) I get the following cast exception in the xquery:eval:
Stopped at [...]/file, 9/31: ---> (this points to the line with the xquery:eval call)
[XPTY0117] Cannot cast xs:untypedAtomic to xs:QName.
I've absolutely no idea why it started giving this error and even looking at the queryplan is too difficult in such a scenario for me. Anyone can help me out?
Thanks,
This is the smallest example of the code I was able to produce:declare function local:declarations($env as map(*)){
string-join(
map:keys($env) ! ( "declare variable $" || . || " external;")
)
};
declare function local:updateenv($envnode as node(), $env){
let $q := local:declarations($env) || serialize($envnode)
let $dynnode := xquery:eval($q, $env)
return
map:new((
map:entry($envnode/data(@name), $dynnode/text()),
$env
))
};
declare function local:buildenv($envnodes as node()*, $env as map(*)) {
if(empty($envnodes)) then $env
else
let $envnode := $envnodes[1]
let $newenv := local:updateenv($envnode, $env)
return map:new(( $newenv, local:buildenv($envnodes[position() > 1],
$newenv)))
};
map:serialize(
local:buildenv((<env name="n1">v1</env>,
<env name="n2">another {{$n1}}</env>
),
map{})
)CG
Christian Grün29 Oct 2014 11:44 #
Hi Marco,
this is a shorter version of the code that causes the error message:
xquery:eval(
'declare variable $x external; $x',
map { attribute a { 'x' }: 'x' }
)It takes some more lines to explain what's going on, though:
* The key of a map is being atomized. In the example, the attribute will be converted to an item of type xs:untypedAtomic
* In earlier versions of the XQuery 3.1 Working Draft, all untyped keys of a maps were cast to strings
* In a more recent version, map keys of type xs:untypedAtomic are not cast anymore
* If
xquery:eval is called, the untyped atomic cannot be cast to a QName ($x)
The easiest solution is to replace "data(@name)" with "string(@name)". I will additionally check if there is a clean way to do the cast implicitly in BaseX.
Hope this helps,
Christian
[1] http://www.w3.org/TR/xquery-31/#doc-xquery31-MapConstructorQuoted text · Marco Lettere · 57 lines
On Wed, Oct 29, 2014 at 10:49 AM, Marco Lettere <marco.lettere at dedalus.eu> wrote: > Hello all, > I've stumbled upon an issue with the latest version and in particular the > code I provide was working with 8.0 snapshot up to build 496C381 and with > all previous official versions of course. Build 151df63 is the first build > I'm aware of the difference. > Shortly. In an input document I have a list of env nodes (sort of <env > name="n">v</env>). They need to be parsed and "xquery:eval(uated)" in > sequence because following env nodes may refer to preceding ones (i.e. <env > name="n1">v1</env>, <env name="n2">{$n1}</env>). > I've managed to do the job with the following code but now (as described in > the preamble) I get the following cast exception in the xquery:eval: > > Stopped at [...]/file, 9/31: ---> (this points to the line with the > xquery:eval call) > [XPTY0117] Cannot cast xs:untypedAtomic to xs:QName. > > I've absolutely no idea why it started giving this error and even looking at > the queryplan is too difficult in such a scenario for me. > Anyone can help me out? > > Thanks, > > This is the smallest example of the code I was able to produce: > > declare function local:declarations($env as map(*)){ > string-join( > map:keys($env) ! ( "declare variable $" || . || " external;") > ) > }; > > declare function local:updateenv($envnode as node(), $env){ > let $q := local:declarations($env) || serialize($envnode) > let $dynnode := xquery:eval($q, $env) > return > map:new(( > map:entry($envnode/data(@name), $dynnode/text()), > $env > )) > }; > > declare function local:buildenv($envnodes as node()*, $env as map(*)) { > if(empty($envnodes)) then $env > else > let $envnode := $envnodes[1] > let $newenv := local:updateenv($envnode, $env) > return map:new(( $newenv, local:buildenv($envnodes[position() > 1], > $newenv))) > }; > > map:serialize( > local:buildenv((<env name="n1">v1</env>, > <env name="n2">another {{$n1}}</env> > ), > map{}) > )
ML
Marco Lettere29 Oct 2014 12:18 #
Wow, that was tricky!
Thanks a lot Christian and yes ... I'll pay defintely more attention to the use of data()/string() in the future!
Cheers,
Marco.
Quoted text · Christian Grün · 88 lines
On 29/10/2014 11:44, Christian Grün wrote: > Hi Marco, > > this is a shorter version of the code that causes the error message: > > xquery:eval( > 'declare variable $x external; $x', > map { attribute a { 'x' }: 'x' } > ) > > It takes some more lines to explain what's going on, though: > > * The key of a map is being atomized. In the example, the attribute > will be converted to an item of type xs:untypedAtomic > * In earlier versions of the XQuery 3.1 Working Draft, all untyped > keys of a maps were cast to strings > * In a more recent version, map keys of type xs:untypedAtomic are not > cast anymore > * If xquery:eval is called, the untyped atomic cannot be cast to a QName ($x) > > The easiest solution is to replace "data(@name)" with "string(@name)". > I will additionally check if there is a clean way to do the cast > implicitly in BaseX. > > Hope this helps, > Christian > > [1] http://www.w3.org/TR/xquery-31/#doc-xquery31-MapConstructor > > > > On Wed, Oct 29, 2014 at 10:49 AM, Marco Lettere > <marco.lettere at dedalus.eu> wrote: >> Hello all, >> I've stumbled upon an issue with the latest version and in particular the >> code I provide was working with 8.0 snapshot up to build 496C381 and with >> all previous official versions of course. Build 151df63 is the first build >> I'm aware of the difference. >> Shortly. In an input document I have a list of env nodes (sort of <env >> name="n">v</env>). They need to be parsed and "xquery:eval(uated)" in >> sequence because following env nodes may refer to preceding ones (i.e. <env >> name="n1">v1</env>, <env name="n2">{$n1}</env>). >> I've managed to do the job with the following code but now (as described in >> the preamble) I get the following cast exception in the xquery:eval: >> >> Stopped at [...]/file, 9/31: ---> (this points to the line with the >> xquery:eval call) >> [XPTY0117] Cannot cast xs:untypedAtomic to xs:QName. >> >> I've absolutely no idea why it started giving this error and even looking at >> the queryplan is too difficult in such a scenario for me. >> Anyone can help me out? >> >> Thanks, >> >> This is the smallest example of the code I was able to produce: >> >> declare function local:declarations($env as map(*)){ >> string-join( >> map:keys($env) ! ( "declare variable $" || . || " external;") >> ) >> }; >> >> declare function local:updateenv($envnode as node(), $env){ >> let $q := local:declarations($env) || serialize($envnode) >> let $dynnode := xquery:eval($q, $env) >> return >> map:new(( >> map:entry($envnode/data(@name), $dynnode/text()), >> $env >> )) >> }; >> >> declare function local:buildenv($envnodes as node()*, $env as map(*)) { >> if(empty($envnodes)) then $env >> else >> let $envnode := $envnodes[1] >> let $newenv := local:updateenv($envnode, $env) >> return map:new(( $newenv, local:buildenv($envnodes[position() > 1], >> $newenv))) >> }; >> >> map:serialize( >> local:buildenv((<env name="n1">v1</env>, >> <env name="n2">another {{$n1}}</env> >> ), >> map{}) >> )
CG
Christian Grün30 Oct 2014 10:31 #
Hi Marco,
I have updated the argument handling of the
xquery:eval function such that items of untyped atomic are accepted as well. Indeed this is the standard behavior of most XQuery functions: if a node or untyped atomic is passed on as argument, it will be implicitly cast to a string.
ChristianQuoted text · Marco Lettere · 106 lines
On Wed, Oct 29, 2014 at 12:18 PM, Marco Lettere <marco.lettere at dedalus.eu> wrote: > Wow, that was tricky! > Thanks a lot Christian and yes ... I'll pay defintely more attention to the > use of data()/string() in the future! > Cheers, > Marco. > > > On 29/10/2014 11:44, Christian Grün wrote: >> >> Hi Marco, >> >> this is a shorter version of the code that causes the error message: >> >> xquery:eval( >> 'declare variable $x external; $x', >> map { attribute a { 'x' }: 'x' } >> ) >> >> It takes some more lines to explain what's going on, though: >> >> * The key of a map is being atomized. In the example, the attribute >> will be converted to an item of type xs:untypedAtomic >> * In earlier versions of the XQuery 3.1 Working Draft, all untyped >> keys of a maps were cast to strings >> * In a more recent version, map keys of type xs:untypedAtomic are not >> cast anymore >> * If xquery:eval is called, the untyped atomic cannot be cast to a QName >> ($x) >> >> The easiest solution is to replace "data(@name)" with "string(@name)". >> I will additionally check if there is a clean way to do the cast >> implicitly in BaseX. >> >> Hope this helps, >> Christian >> >> [1] http://www.w3.org/TR/xquery-31/#doc-xquery31-MapConstructor >> >> >> >> On Wed, Oct 29, 2014 at 10:49 AM, Marco Lettere >> <marco.lettere at dedalus.eu> wrote: >>> >>> Hello all, >>> I've stumbled upon an issue with the latest version and in particular the >>> code I provide was working with 8.0 snapshot up to build 496C381 and with >>> all previous official versions of course. Build 151df63 is the first >>> build >>> I'm aware of the difference. >>> Shortly. In an input document I have a list of env nodes (sort of <env >>> name="n">v</env>). They need to be parsed and "xquery:eval(uated)" in >>> sequence because following env nodes may refer to preceding ones (i.e. >>> <env >>> name="n1">v1</env>, <env name="n2">{$n1}</env>). >>> I've managed to do the job with the following code but now (as described >>> in >>> the preamble) I get the following cast exception in the xquery:eval: >>> >>> Stopped at [...]/file, 9/31: ---> (this points to the line with the >>> xquery:eval call) >>> [XPTY0117] Cannot cast xs:untypedAtomic to xs:QName. >>> >>> I've absolutely no idea why it started giving this error and even looking >>> at >>> the queryplan is too difficult in such a scenario for me. >>> Anyone can help me out? >>> >>> Thanks, >>> >>> This is the smallest example of the code I was able to produce: >>> >>> declare function local:declarations($env as map(*)){ >>> string-join( >>> map:keys($env) ! ( "declare variable $" || . || " external;") >>> ) >>> }; >>> >>> declare function local:updateenv($envnode as node(), $env){ >>> let $q := local:declarations($env) || serialize($envnode) >>> let $dynnode := xquery:eval($q, $env) >>> return >>> map:new(( >>> map:entry($envnode/data(@name), $dynnode/text()), >>> $env >>> )) >>> }; >>> >>> declare function local:buildenv($envnodes as node()*, $env as map(*)) { >>> if(empty($envnodes)) then $env >>> else >>> let $envnode := $envnodes[1] >>> let $newenv := local:updateenv($envnode, $env) >>> return map:new(( $newenv, local:buildenv($envnodes[position() > 1], >>> $newenv))) >>> }; >>> >>> map:serialize( >>> local:buildenv((<env name="n1">v1</env>, >>> <env name="n2">another {{$n1}}</env> >>> ), >>> map{}) >>> ) > >
Written for BaseX 7.9. BaseX may have changed since: please check the current documentation.
⚡Generated with XQuery