Xpath 如何计算递归XQuery函数中的匹配元素
下面的代码可以工作,但现在我想指定xpath指定的元素的哪个出现项将受到影响(替换、删除、插入等) 并获得以下输出:Xpath 如何计算递归XQuery函数中的匹配元素,xpath,xquery,Xpath,Xquery,下面的代码可以工作,但现在我想指定xpath指定的元素的哪个出现项将受到影响(替换、删除、插入等) 并获得以下输出: <fo:Test xmlns:fo="http://www.w3.org/1999/XSL/Format"> <fo:books> <!-- These are my books --> <book title='xQuery for Dummys'> <author>Jack Wiza
<fo:Test xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:books>
<!-- These are my books -->
<book title='xQuery for Dummys'>
<author>Jack Wizard</author>
<details>
<pages>221</pages>
</details>
</book>
<book title='Mysteries of xQuery'>
<author>Lost Linda</author>
<details>
<replace-this>Goodbye World!</replace-this>
<replaced>Hello World!</replaced>
</details>
</book>
</fo:books>
</fo:Test>
有没有办法记录我得到的正匹配数并将其添加到条件中?我被难住了 下面提供了一个与您试图实现的类似的示例,它通过使用递归和在路径运行时记忆路径来实现,以便与您希望替换的路径进行比较 这使用了XQuery 3.1,但是可以在XQuery 1.0中通过编码/解码序列或内存中XML文档的映射结构来重写它
xquery version "3.1";
declare namespace fo="http://www.w3.org/1999/XSL/Format";
declare %private function local:push-element($path-stack as map(xs:integer, map(xs:QName, xs:integer)), $element as element(), $depth as xs:integer) as map(xs:integer, map(xs:QName, xs:integer)) {
let $occurence-map := $path-stack($depth)
return
if(not(empty($occurence-map))) then
let $name-occurs := $occurence-map(node-name($element))
return
if($name-occurs) then
map:put($path-stack, $depth, map:put($occurence-map, node-name($element), $name-occurs + 1))
else
map:put($path-stack, $depth, map:put($occurence-map, node-name($element), 1))
else
map:put($path-stack, $depth, map { node-name($element) : 1 })
(: TODO to reduce memory you could remove (pop) any elements from the map which have depth gt $depth :)
};
declare %private function local:children($children as node()*, $replace as xs:string, $replacement as element(), $current-path as xs:QName*, $path-stack as map(xs:integer, map(xs:QName, xs:integer))) {
if($children)then
let $child := $children[1]
return
let $new-path-stack :=
if($child instance of element())then
local:push-element($path-stack, $child, count($current-path) + 1)
else
$path-stack
return
(
local:replace($child, $replace, $replacement, $current-path, $new-path-stack),
local:children(subsequence($children, 2), $replace, $replacement, $current-path, $new-path-stack)
)
else()
};
declare %private function local:get-occurence($name as xs:QName, $depth as xs:integer, $path-stack as map(xs:integer, map(xs:QName, xs:integer))) {
$path-stack($depth)($name)
};
declare %private function local:eq-path-with-positions($current-path as xs:QName*, $path-stack as map(xs:integer, map(xs:QName, xs:integer)), $path-with-positions as xs:string) as xs:boolean {
let $current-path-with-positions :=
'/' || string-join(
for $step-qn at $depth in $current-path
let $occurence := local:get-occurence($step-qn, $depth, $path-stack)
let $occurence-predicate-str := '[' || $occurence || ']'
return
$step-qn || $occurence-predicate-str
, '/'
)
return
$path-with-positions eq $current-path-with-positions
};
declare %private function local:replace($node as node(), $replace as xs:string, $replacement as element(), $current-path as xs:QName*, $path-stack as map(xs:integer, map(xs:QName, xs:integer))) as node() {
typeswitch($node)
case document-node()
return
document {
$node/node() ! local:replace(., $replace, $replacement, $current-path, $path-stack)
}
case element()
return
let $new-path := ($current-path, node-name($node))
let $new-path-stack :=
if(map:size($path-stack) eq 0) then
local:push-element($path-stack, $node, count($new-path))
else
$path-stack
return
let $matched-for-replace := local:eq-path-with-positions($new-path, $new-path-stack, $replace)
return
if($matched-for-replace)then
$replacement
else
element {name($node)} {
$node/@*,
local:children($node/node(), $replace, $replacement, $new-path, $new-path-stack)
}
default return $node
};
declare function local:replace($node as node(), $replace as xs:string, $replacement as element()) as node() {
let $path-stack as map(xs:integer, map(xs:QName, xs:integer)) := map {}
return
local:replace($node, $replace, $replacement, (), $path-stack)
};
let $doc :=
<fo:Test xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:books>
<!-- These are my books -->
<book title='xQuery for Dummys'>
<author>Jack Wizard</author>
<details>
<pages>221</pages>
</details>
</book>
<book title='Mysteries of xQuery'>
<author>Lost Linda</author>
<details>
<replace-this>Goodbye World!</replace-this>
<pages>40</pages>
</details>
</book>
</fo:books>
</fo:Test>
return
local:replace($doc, '/fo:Test[1]/fo:books[1]/book[2]/details[1]/pages[1]', <replaced>Hello World!</replaced>)
xquery版本“3.1”;
声明命名空间fo=”http://www.w3.org/1999/XSL/Format";
将%private function local:push元素($path stack as map(xs:integer,map(xs:QName,xs:integer)),$element as element(),$depth as xs:integer)声明为map(xs:integer,map(xs:QName,xs:integer)){
let$occurrence-map:=$path-stack($depth)
返回
如果(不是(空($occurrence map)),则
让$name发生:=$occurrence映射(节点名称($element))
返回
如果($name出现),则
map:put($path-stack,$depth,map:put($occurrence-map,node-name($element),$name-occurrence+1))
其他的
map:put($path stack,$depth,map:put($occurrence map,node name($element),1))
其他的
map:put($path stack,$depth,map{node name($element):1})
(:TODO为减少内存,您可以从地图中删除(弹出)深度大于$depth:的任何元素)
};
声明%private function local:children($children as node()*,$replace as xs:string,$replacement as element(),$current path as xs:QName*,$path stack as map(xs:integer,map(xs:QName,xs:integer))){
如果($children)那么
let$child:=$children[1]
返回
让$new path堆栈:=
如果($element()的子实例),则
本地:推送元素($path stack,$child,count($current path)+1)
其他的
$path堆栈
返回
(
本地:替换($child、$replace、$replacement、$current path、$new path stack),
本地:子级(子序列($children,2),$replace,$replacement,$current path,$new path堆栈)
)
else()
};
声明%private函数local:get-occurrence($name为xs:QName,$depth为xs:integer,$path-stack为map(xs:integer,map(xs:QName,xs:integer))){
$path堆栈($depth)($name)
};
将位置为($current path作为xs:QName*,$path stack作为map(xs:integer,map(xs:QName,xs:integer)),位置为xs:string的$path作为xs:boolean的%private function local:eq path声明为xs:boolean{
让$current path包含位置:=
“/”| |字符串连接(
对于$current path中$depth处的$step qn
let$occurrence:=local:get-occurrence($step-qn,$depth,$path-stack)
让$occurrence谓词str:='['| | |$occurrence |']
返回
$step qn | |$occurrence谓词str
, '/'
)
返回
$path with positions eq$current path with positions
};
将%private function local:replace($node as node(),$replace as xs:string,$replace as element(),$current path as xs:QName*,$path stack as map(xs:integer,map(xs:QName,xs:integer))声明为node(){
类型开关($node)
案例文档-node()
返回
文件{
$node/node()!local:replace(,$replace,$replacement,$current path,$path stack)
}
case元素()
返回
让$new path:=($current path,node name($node))
让$new path堆栈:=
如果(映射:大小($path stack)等式0),则
本地:推送元素($path stack,$node,count($new path))
其他的
$path堆栈
返回
让$matched for replace:=local:eq路径与位置($new path,$new path stack,$replace)
返回
如果($匹配替换),则
$replacement
其他的
元素{name($node)}{
$node/@*,
本地:子节点($node/node(),$replace,$replacement,$new path,$new path stack)
}
默认返回$node
};
将函数local:replace($node作为node(),$replace作为xs:string,$replacement作为element())声明为node(){
让$path堆栈为map(xs:integer,map(xs:QName,xs:integer)):=map{}
返回
本地:替换($node,$replace,$replacement,(),$path堆栈)
};
让$doc:=
杰克巫师
221
迷路的琳达
再见,世界!
40
返回
本地:替换($doc,'/fo:Test[1]/fo:books[1]/book[2]/details[1]/pages[1]',Hello World!)
在我的理解中,路径/fo:Test/fo:books/book/details/pages[2]
没有在示例XML中选择任何内容,因为根本没有details
元素有第二个pages
子元素。但是应该使用该路径选择哪个元素/fo:Test/fo:books/book/details/pages[2]
?在你的样品中没有。您可以使用例如/fo:Test/fo:books/book[2]/details/pages
或(/fo:Test/fo:books/book/details/pages)[2]
。它也希望选择我想要的内容。由我来定义输入的语法。这只是传递引用值的一种方式,而不将其作为另一个参数。但正如我期望的输出所示,如果它不是一个递归函数和一个简单的xpath选择问题,我们将使用(/fo:Test/fo:books/book/details/pages)[2]
,那么您不应该调用它“在xpath中传递我想要的事件”.编辑问题,使出现值成为函数的单独参数,而不是路径的一部分
<fo:Test xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:books>
<!-- These are my books -->
<book title='xQuery for Dummys'>
<author>Jack Wizard</author>
<details>
<pages>221</pages>
</details>
</book>
<book title='Mysteries of xQuery'>
<author>Lost Linda</author>
<details>
<replace-this>Goodbye World!</replace-this>
<replaced>Hello World!</replaced>
</details>
</book>
</fo:books>
</fo:Test>
<fo:Test xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:books>
<!-- These are my books -->
<book title='xQuery for Dummys'>
<author>Jack Wizard</author>
<details>
<replace-this>Goodbye World!</replace-this>
</details>
</book>
<book title='Mysteries of xQuery'>
<author>Lost Linda</author>
<details>
<replace-this>Goodbye World!</replace-this>
<replaced>Hello World!</replaced>
</details>
</book>
</fo:books>
</fo:Test>
xquery version "3.1";
declare namespace fo="http://www.w3.org/1999/XSL/Format";
declare %private function local:push-element($path-stack as map(xs:integer, map(xs:QName, xs:integer)), $element as element(), $depth as xs:integer) as map(xs:integer, map(xs:QName, xs:integer)) {
let $occurence-map := $path-stack($depth)
return
if(not(empty($occurence-map))) then
let $name-occurs := $occurence-map(node-name($element))
return
if($name-occurs) then
map:put($path-stack, $depth, map:put($occurence-map, node-name($element), $name-occurs + 1))
else
map:put($path-stack, $depth, map:put($occurence-map, node-name($element), 1))
else
map:put($path-stack, $depth, map { node-name($element) : 1 })
(: TODO to reduce memory you could remove (pop) any elements from the map which have depth gt $depth :)
};
declare %private function local:children($children as node()*, $replace as xs:string, $replacement as element(), $current-path as xs:QName*, $path-stack as map(xs:integer, map(xs:QName, xs:integer))) {
if($children)then
let $child := $children[1]
return
let $new-path-stack :=
if($child instance of element())then
local:push-element($path-stack, $child, count($current-path) + 1)
else
$path-stack
return
(
local:replace($child, $replace, $replacement, $current-path, $new-path-stack),
local:children(subsequence($children, 2), $replace, $replacement, $current-path, $new-path-stack)
)
else()
};
declare %private function local:get-occurence($name as xs:QName, $depth as xs:integer, $path-stack as map(xs:integer, map(xs:QName, xs:integer))) {
$path-stack($depth)($name)
};
declare %private function local:eq-path-with-positions($current-path as xs:QName*, $path-stack as map(xs:integer, map(xs:QName, xs:integer)), $path-with-positions as xs:string) as xs:boolean {
let $current-path-with-positions :=
'/' || string-join(
for $step-qn at $depth in $current-path
let $occurence := local:get-occurence($step-qn, $depth, $path-stack)
let $occurence-predicate-str := '[' || $occurence || ']'
return
$step-qn || $occurence-predicate-str
, '/'
)
return
$path-with-positions eq $current-path-with-positions
};
declare %private function local:replace($node as node(), $replace as xs:string, $replacement as element(), $current-path as xs:QName*, $path-stack as map(xs:integer, map(xs:QName, xs:integer))) as node() {
typeswitch($node)
case document-node()
return
document {
$node/node() ! local:replace(., $replace, $replacement, $current-path, $path-stack)
}
case element()
return
let $new-path := ($current-path, node-name($node))
let $new-path-stack :=
if(map:size($path-stack) eq 0) then
local:push-element($path-stack, $node, count($new-path))
else
$path-stack
return
let $matched-for-replace := local:eq-path-with-positions($new-path, $new-path-stack, $replace)
return
if($matched-for-replace)then
$replacement
else
element {name($node)} {
$node/@*,
local:children($node/node(), $replace, $replacement, $new-path, $new-path-stack)
}
default return $node
};
declare function local:replace($node as node(), $replace as xs:string, $replacement as element()) as node() {
let $path-stack as map(xs:integer, map(xs:QName, xs:integer)) := map {}
return
local:replace($node, $replace, $replacement, (), $path-stack)
};
let $doc :=
<fo:Test xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:books>
<!-- These are my books -->
<book title='xQuery for Dummys'>
<author>Jack Wizard</author>
<details>
<pages>221</pages>
</details>
</book>
<book title='Mysteries of xQuery'>
<author>Lost Linda</author>
<details>
<replace-this>Goodbye World!</replace-this>
<pages>40</pages>
</details>
</book>
</fo:books>
</fo:Test>
return
local:replace($doc, '/fo:Test[1]/fo:books[1]/book[2]/details[1]/pages[1]', <replaced>Hello World!</replaced>)