使用 XQuery 删除 xml 个节点

Remove xml nodes using XQuery

我有以下 xml:

<?xml version="1.0" encoding="UTF-8"?>
<publication>
    <scientificPublication>
        <id>2347823476</id>
        <book>
            <chapter>
                <author>Tom</author>
                <genre>Java</genre>
                <genre>Webservice</genre>
            </chapter>
        </book>
        <book>
            <chapter>
                <author>Mike</author>
                <genre>C</genre>
                <genre>Machine Learning</genre>
            </chapter>
        </book>
        <book>
            <chapter>
                <author>William</author>
                <genre>JavaScript</genre>
                <genre>Frontend</genre>
            </chapter>
        </book>
    </scientificPublication>
</publication>

我需要使用 XQuery 过滤 xml,所以最终得到以下结果:

<?xml version="1.0" encoding="UTF-8"?>
<publication>
    <scientificPublication>
        <id>2347823476</id>
        <book>
            <chapter>
                <author>Tom</author>
                <genre>Java</genre>
                <genre>Webservice</genre>
            </chapter>
        </book>
    </scientificPublication>
</publication>

基本上我的想法是找到第一本书中的所有兄弟节点,然后使用函数 fn:remove((item,item,...),position) 删除它们。所以我用下面的xpath找到了所有需要删除的节点:

publication/*/book[not(./*[(genre='Java')and(genre='Webservice')])]

然后我尝试使用以下代码将它们从 xml 中删除:

declare function local:process-xml($element) {
   let $orig := $element
   let $toFilter := $element/*/book[not(./*[(genre='Java')and(genre='Webservice')])]

   let $el := local:filter($orig, $toFilter) 
   return $el
};

declare function local:filter($elements, $toFilter) {
    for $i in $toFilter
        let $pos := position()
        remove($lements, $pos)
    return $elements
};

当我尝试执行此操作时,我收到一条错误消息,提示他已停止,期望在第二个函数上出现 'return'。 有人知道我在这种情况下做错了什么吗?

您的整个方法本质上感觉不太实用。使用 BaseX 删除元素最好使用 XQuery Update.

因此,在您的情况下,您只需使用以下函数删除所有书籍元素:

declare function local:process-xml($element) {
  $element update delete node .//book[not(./*[(genre='Java')and(genre='Webservice')])] 
};

您可以使用递归函数过滤掉 book 个元素:

declare function local:filter($node)
{
  typeswitch($node)
    case element(book) return
      if ($node[not(*[(genre='Java') and (genre='Webservice')])])
      then () (: we don't want these books, so return empty sequence :)
      else $node (: this is the book we want :)
    case element() return 
      element { fn:node-name($node) } {
        $node/@*,
        $node/node() ! local:filter(.)
      }
    default return $node
};