Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/vba/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Xpath 如何计算递归XQuery函数中的匹配元素_Xpath_Xquery - Fatal编程技术网

Xpath 如何计算递归XQuery函数中的匹配元素

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

下面的代码可以工作,但现在我想指定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>

有没有办法记录我得到的正匹配数并将其添加到条件中?我被难住了

下面提供了一个与您试图实现的类似的示例,它通过使用递归和在路径运行时记忆路径来实现,以便与您希望替换的路径进行比较

这使用了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>)