根据XQuery

时间:2016-11-27 18:37:48

标签: xml xquery marklogic

我需要一个能够重写文档的XQuery函数,特别是返回子节点的子集,基于指定要检索的节点名称的字符串数组。如果应该满足如下定义并且需要使用任何任意文档。

local:apply-node-includes($document, $includedNodeNames as xs:string*)

鉴于我有一些xml doc

let $doc := 
  <foo a="b">
    <bar>hello</bar>
    <baz>1</baz>
    <bang>
      <text>world</text>
    </bang>
  </foo>')

然后该函数应该转换文档,以便只返回在$ includedNodes中指定名称的子节点。

示例local:apply-node-includes($doc, 'baz')将返回

<foo a="b">
  <baz>1</baz>
</foo>

示例local:apply-node-includes($doc, ('bar','bang')))将返回

<foo a="b">
  <bar>hello</bar>
  <bang>
    <text>world</text>
  </bang>
</foo>

我尝试迭代节点,和/或使用某种形式的递归类型切换,但到目前为止还无法做到正确。如果它完全递归地工作会非常酷,所以'bang.text'将只包括孙子文本节点,而不是它们的任何兄弟节点,但也许这要求太多了!

2 个答案:

答案 0 :(得分:1)

我不确定这是否是最优雅的解决方案,但似乎符合您的要求。此处的函数重新创建传递文档的根元素,然后包含任何直接子元素(及其所有属性和子元素),其元素名称与传递列表中的任何字符串匹配。

declare function local:apply-node-includes( $doc as item(), $includedNodeNames as xs:string*) as item()
{
   (: Recreate the root element :)
   element {name($doc)} 
   { (: Replicate root element's attributes :)
     (for    $attribute in $doc/@* return $attribute),
     (: Replicate root element's immediate children having any of given names :)
     (for    $element in $doc/* where name($element) = $includedNodeNames 
      return $element)
   }
};

let $doc := 
  <foo a="b">
    <bar>hello</bar>
    <baz>1</baz>
    <bang>
      <text>world</text>
    </bang>
  </foo>
return local:apply-node-includes($doc, ('bar','bang'))

输出:

<foo a="b"><bar>hello</bar><bang><text>world</text></bang></foo>

答案 1 :(得分:1)

@DavidDeneberg给出了一个非常好的答案,但我设法使用一些xpath进一步简化它,以便发布给其他人。

declare function local:apply-node-includes($doc as element(), $includedNodeNames as xs:string*) as element()?
{
  element {node-name($doc) }
  {
    $doc/@*,
    $doc/*[name(.)=$include-names]
  }
};

这本书对这个主题非常有用 https://en.wikibooks.org/wiki/XQuery/Filtering_Nodes并演示了你需要能够处理孙子问题部分的递归身份转换。