以下代码有效,但现在我想指定一个参数,即xpath指定的元素的出现会受到影响(替换,删除,插入等)
declare function local:replace($doc as node(), $new-content as item()*,
$target-path as xs:string) {
local:transform($doc, $new-content, $target-path, 'replace', "")
};
declare function local:transform($node as node(), $new-content as item()*,
$target-path as xs:string, $action as xs:string, $path as xs:string)
as node()+ {
if ($node instance of element() and concat($path, "/", node-name($node)) = $target-path)
then
if ($action = 'insert-before')
then ($new-content, $node)
else
if ($action = 'insert-after')
then ($node, $new-content)
else
if ($action = 'insert-as-child')
then element {node-name($node)}
{
$node/@*
,
$new-content
,
for $child in $node/node()
return $child
}
else
if ($action = 'replace')
then $new-content
else
if ($action = 'delete')
then ()
else ()
else
if ($node instance of element())
then
element {node-name($node)}
{
$node/@*
,
for $child in $node/node()
return
local:transform($child, $new-content, $target-path, $action, concat($path, "/", node-name($node)))
}
else $node
};
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>
(: -------- My test -------- :)
let $new-content := <replaced>Hello World!</replaced> return
local:replace($doc, $new-content, '/fo:Test/fo:books/book/details/replace-this')
显然$global-counter = $global-counter + 1
无法正常工作。我想传递我想要的事件作为另一个参数,如:
local:replace($doc, $new-content, '/fo:Test/fo:books/book/details/pages', 2)
获得此输出:
<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>
我使用local:replace($doc, $new-content, '/fo:Test/fo:books/book/details/pages')
<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>
有没有办法记录我得到的积极匹配数量并将其添加到条件中?我很难过!
答案 0 :(得分:1)
下面提供了一个类似于您要实现的示例,它通过使用递归和记忆路径来实现,以便它可以与您希望替换的路径进行比较。
这使用XQuery 3.1,但是可以通过将映射结构编码/解码到序列或内存中的XML文档来在XQuery 1.0中重写。
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>)