1
votes

I have the following 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>

I need to filter the xml using XQuery, so in the end, I have the following resoult:

<?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>

So basically my idea, was to find all the sibling nodes from the first book, and then remove them with the function fn:remove((item,item,...),position). So I found all the nodes that need to be removed with the following xpath:

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

And then I try to remove them from the xml using the code:

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
};

When I try to execute this, I get an error saying that he stopped, expecting a 'return' on the second function. Anyone knows what I'm doing wrong in this case?

2

2 Answers

2
votes

Your whole approach does not feel very functional in nature. Removing elements using BaseX is done best using XQuery Update.

So in your case you simply delete all book elements using the following function:

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

You could use a recursive function that filters out the book elements:

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
};