xmlxqueryxquery-3.0xquery-3.1

XQuery to remove all elements with attribute onlyChannels="print" from XML file


Trying to remove all elements with attribute onlyChannels="print" from XML Using XQuery the element with onlyChannels="print" can be anywhere and at different levels.

Input XML

<?xml version="1.0" encoding="UTF-8"?>
<abstractGroup>
   <abstract type="main" xml:lang="en">
      <title type="main">Abstract</title>
      <p>900 000 ha along the test of north</p>
      <p onlyChannels="print">Abstract</p>
   </abstract>
   <abstract onlyChannels="online" type="main" xml:lang="es">
      <title type="main">Resumen</title>
      <p>La orsdft de los trópifdaa</p>
   </abstract>
   <full type="main" xml:lang="en">
      <p onlyChannels="print">full</p>
      <p>900 000 ha along the test of north‐east</p>
      <doc>
      <p onlyChannels="print"> do not print</p>
      <p> print </p>
      </doc>
   </full>
</abstractGroup>

expcted output XML

<abstractGroup>
   <abstract type="main" xml:lang="en">
      <title type="main">Abstract</title>
      <p>900 000 ha along the test of north</p>
   </abstract>
   <abstract onlyChannels="online" type="main" xml:lang="es">
      <title type="main">Resumen</title>
      <p>La orsdft de los trópifdaa</p>
   </abstract>
   <full type="main" xml:lang="en">
      <p>900 000 ha along the test of north‐east</p>
     <doc>
      <p> print </p>
      </doc>
   </full>
</abstractGroup>

I was trying this XQuery but it's only removing elements in the first level and without XML tags.

let $root:=  abstractGroup/*/*[not(self::*/@onlyChannels="print")]
return $root

what I got :

Abstract 
900 000 ha along the test of north 
Resumen 
La orsdft de los trópifdaa
900 000 ha along the test of north‐east

   do not print
   print

How I can print the xml tags and removing all elements with attribute onlyChannels="print"


Solution

  • You could run it through a recursive typeswitch function to transform the XML:

    declare function local:filter($nodes as node()*) as node()*
    {
      for $n in $nodes return
      typeswitch ($n)
        case element () return 
          if ($n[@onlyChannels="print"]) 
          then local:filter($n/node()) 
          else element { node-name($n) } { $n/@*, local:filter($n/node())}
        default return $n
    };
    
    let $doc :=
    <abstractGroup>
       <abstract type="main" xml:lang="en">
          <title type="main">Abstract</title>
          <p>900 000 ha along the test of north</p>
          <p onlyChannels="print">Abstract</p>
       </abstract>
       <abstract onlyChannels="online" type="main" xml:lang="es">
          <title type="main">Resumen</title>
          <p>La orsdft de los trópifdaa</p>
       </abstract>
       <full type="main" xml:lang="en">
          <p onlyChannels="print">full</p>
          <p>900 000 ha along the test of north‐east</p>
          <doc>
          <p onlyChannels="print"> do not print</p>
          <p> print </p>
          </doc>
       </full>
    </abstractGroup>
    
    return local:filter($doc)