Is there a way to do something like this?
declare function local:insert( $doc as node(), $at-location as xpath(), (: I know, there is no 'xpath' type, this is just for demo :) $what as node()* ) as node() { copy $c := $doc modify insert node $what into $at-location return $c };
let $xml := <xml><body><p>This is a paragraph.</p></body></xml> let $nd := <this><is><a>Test</a></is></this> return local:insert($xml, xpath("/body"), $nd)
As I see it, it would need a dynamic xs:string => XPath expression conversion or a defer XPath expression, which both do not exist, for $at-location. I managed to do this, however:
declare function local:insert($doc, $at-location, $what) { let $update := ``[ declare variable $doc external; declare variable $what external; declare variable $at-location external; copy $c := $doc modify insert node $what into `{$at-location}` return $c ]`` return xquery:eval($update, map{ 'doc':$doc, 'at-location':$at-location, '$what':$what }) };
let $xml := <xml><body><p>This is a paragraph.</p></body></xml> let $node := <this><is><a>Test</a></is></this> return local:insert($xml, "$c/body", $node)
Ideally (for me), there would be a 'defer' XPath expression, that starts its life as a string (when passed) and then unfolds upon call. Or a type (or function) fn:xpath(), that behaves that way.
Or am I just blind and don't see the obvious, and there *is* a simpler solution?
I just realized, that there is another, simpler way to simulate a defer XPath expression. So I ended up using:
declare function local:add-node( $input as node()*, $what as node()* ) as node() { copy $c := $input modify insert node $what into $c return $c }; declare function local:add-node( $input as node()*, $what as node()*, $at-location as xs:string ) as node() { copy $c := $input modify let $xpath := xquery:eval($at-location, map{"":$c}) return insert node $what into $c/$xpath return $c };
<html></html> => local:add-node((<head></head>,<body></body>)) => local:add-node(<title>Example Title</title>, "head")
Hi Andreas,
If your dynamic path is simple, you can get rid of xquery:eval and replace it with a fold-left iteration (see the attached example). The programmatic solution may be safer, in particular if the supplied path may be the result of external user input.
Obviously, you’ll need to ensure in both cases that the resulting target node is a single element node.
Cheers, Christian
declare function local:add-nodes1( $input as node(), $nodes as node()*, $path as xs:string ) as node() { $input update { let $target := xquery:eval($path, map { '': . }) return insert nodes $nodes into $target } };
declare function local:add-nodes2( $input as node(), $nodes as node()*, $path as xs:string ) as node() { $input update { let $steps := tokenize($path, '/') let $target := fold-left($steps, ., function($node, $name) { $node/*[name() = $name] }) return insert nodes $nodes into $target } };
<xml><a><b/></a></xml> => local:add-nodes1(<new1/>, 'a/b') => local:add-nodes2(<new2/>, 'a/b')
On Sun, Oct 28, 2018 at 5:19 PM Andreas Mixich mixich.andreas@gmail.com wrote:
I just realized, that there is another, simpler way to simulate a defer XPath expression. So I ended up using:
declare function local:add-node( $input as node()*, $what as node()* ) as node() { copy $c := $input modify insert node $what into $c return $c }; declare function local:add-node( $input as node()*, $what as node()*, $at-location as xs:string ) as node() { copy $c := $input modify let $xpath := xquery:eval($at-location, map{"":$c}) return insert node $what into $c/$xpath return $c };
<html></html> => local:add-node((<head></head>,<body></body>)) => local:add-node(<title>Example Title</title>, "head")
basex-talk@mailman.uni-konstanz.de