Search code examples
xmlxquerybasex

Remove xml nodes using XQuery


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?


Solution

  • 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')])] 
    };