Xquery 如何遍历内存中的XML结构并替换子结构?

Xquery 如何遍历内存中的XML结构并替换子结构?,xquery,marklogic,marklogic-8,Xquery,Marklogic,Marklogic 8,我有一份文件- <something> <parent> <child>Bird is the word 1.</child> <child>Curd is the word 2.</child> <child>Nerd is the word 3.</child> </parent> <parent> <child>Bi

我有一份文件-

<something>
  <parent>
    <child>Bird is the word 1.</child>
    <child>Curd is the word 2.</child>
    <child>Nerd is the word 3.</child>
  </parent>
  <parent>
    <child>Bird is the word 4.</child>
    <child>Word is the word 5.</child>
    <child>Bird is the word 6.</child>
  </parent>
</something>  
我想遍历文档,并使用XQuery和MarkLogicAPI将Bird这个词替换为Dog。到目前为止,我能够通过以下代码实现:

let $doc := $DOC 
  return <something>
           {for $d at $y in $doc/element()
             let $p := <parent>
                       {for $c in $d/element()
                         let $child := if(fn:matches($c, "Bird")) then(<child>{fn:replace($c, "Bird", "Dog")}</child>) else($c)
                         return $child
                       }</parent>
             return $p}
         </something>
结果是

<something>
  <parent>
    <child>Dog is the word 1.</child>
    <child>Curd is the word 2.</child>
    <child>Nerd is the word 3.</child>
  </parent>
  <parent>
    <child>Dog is the word 4.</child>
    <child>Word is the word 5.</child>
    <child>Dog is the word 6.</child>
  </parent>
</something>

如果没有嵌套for循环,如何实现这一点?以前有人问过这个问题,但使用XSLT。

编写函数并使用递归。使用typeswitch表达式,您可以在递归的每个阶段检查节点类型,并且可以使用通用模板在不知道其名称的情况下重建每个元素:

declare function local:transform(
  $node as node()
) as node()*
{
  typeswitch ($node)
  case element() return element { node-name($node) } {
    $node/@*, 
    for $n in $node/node()
    return local:transform($n)
  }
  case text() return 
    if (matches($node, "Bird"))
    then text { replace($node, "Bird", "Dog") }
    else $node
  default return $node
};

请注意,不需要显式检查匹配项,因为如果没有匹配项,replace将返回输入字符串。

编写函数并使用递归。使用typeswitch表达式,您可以在递归的每个阶段检查节点类型,并且可以使用通用模板在不知道其名称的情况下重建每个元素:

declare function local:transform(
  $node as node()
) as node()*
{
  typeswitch ($node)
  case element() return element { node-name($node) } {
    $node/@*, 
    for $n in $node/node()
    return local:transform($n)
  }
  case text() return 
    if (matches($node, "Bird"))
    then text { replace($node, "Bird", "Dog") }
    else $node
  default return $node
};

请注意,显式检查匹配项是不必要的,因为如果没有匹配项,replace将返回输入字符串。

wst的答案看起来很好,但同样的问题经常被问到,他们创建了一个库来简化这一过程。它通常被称为“内存更新库”。可在以下位置找到改进的版本:

我想至少值得一提


wst的答案看起来很好,但同样的问题经常被问到,他们创建了一个库来简化这个过程。它通常被称为“内存更新库”。可在以下位置找到改进的版本:

我想至少值得一提


为什么不使用s/Bird/Dog/g这样的正则表达式呢?它将在线性时间内一次完成。@wontonimo虽然可以对序列化的XML进行字符串操作,但这被认为是一种不好的做法。确保只在实际需要的地方应用更改也要困难得多。使用单传递字符串替换,很难确保只更改子元素的内容,而不更改其他元素或属性的内容。更重要的是,如果不小心重命名了XML标记,或者更糟的是,导致它们被破坏或删除,则不会破坏XML的良好格式。@grtjn-同意,不过您可以像so s/\>[^\n为什么不使用像s/Bird/Dog/g这样的正则表达式呢?它将在一次传递中以线性时间完成。@wontonimo虽然可以对序列化的XML进行字符串操作,但这被认为是一种不好的做法。要确保只在实际需要的地方应用更改也要困难得多。如果使用单次传递字符串替换,它将非常有用很难确保只更改子元素的内容,而不更改其他元素或属性的内容。更重要的是,不会因为不小心重命名XML标记而破坏XML的良好格式,或者更糟,导致它们被破坏或删除。@grtjn-同意,尽管您可以添加XML标记签入正则表达式,如so s/\>[^\document node添加一个case,并在复制元素以进行更好的标识转换时包含$node/namespace::*。@grtjn是的,同意,我只是为了回答问题的简洁而将其排除在外。另外,对于性能关键型转换,我尝试排除namespace::*除非它是严格需要的,因为我注意到在Ma中rkLogic,根据文档的大小和内容,在每个元素上通配符名称空间轴可能有点重要。$node/namespace::*应该只查看本地声明。我必须尝试一下,但如果这对性能有很大影响,我会感到惊讶。但我下次使用时会记住这一点。@grtjn性能对性能的影响很小。在我的情况下,对一组分页的搜索结果的影响大约为10毫秒。这只在延迟非常紧的预算查询中才有意义,就像在那个应用程序中一样。为文档节点添加一个案例,并包括$node/命名空间::*在复制元素以实现更好的身份转换时。@grtjn是的,同意,我只是为了方便起见才将其排除在外回答问题的简洁性。此外,对于性能关键型转换,我尝试排除名称空间::*除非严格需要它,因为我注意到在MarkLogic中,根据文档的大小和内容,在每个元素上通配符名称空间轴可能有点重要。$node/namespace::*应该考虑本地仅声明。我必须尝试一下,但如果这对性能有很大影响,我会感到惊讶。但我会记住,下次我玩这个游戏时,..@grtjn对性能的影响很小。在我的情况下,搜索结果的页面集大约为10毫秒。这只会在非常紧张的延迟预算查询中起作用,就像在那个应用程序中一样N