1
votes

I have an XML from end system which require to remove all empty elements or attributes that have empty or null value.

Example

<Element>
    <fn att=""></fn>
    <fn att="1"></fn>
    <fn></fn>
    <fn att="">
      <child>1</child>
    </fn>
</Element>

This is my script given using XQuery to check and remove them

declare function local:remove-empty-elements($nodes as node()*)  as node()* {
   for $node in $nodes
   return
     if ($node instance of element())
     then if (
       (normalize-space($node) = '') 
       and (not(normalize-space($node/@*)))   
     )
          then ()
          else element { node-name($node)
        }
       { $node/@*, local:remove-empty-elements($node/node())}
     else if ($node instance of document-node())
          then local:remove-empty-elements($node/node())
     else $node
};

This is successfully remove the elements and tag node that are single. But when i put multiple attributes in one element

e.g. <fn att1="Data" att2="1"></fn>

The function itself fails.

Any ideas how to achieve this?

Thank you before.

** UPDATE **

As per har07 answer, if i want to change the function to remove the attribute that having empty value, is there any suggestions? I try with the logic if below but still sending the same result.

declare function local:remove-empty-elements($nodes as node()*)  as node()* {
   for $node in $nodes
   return
     if ($node instance of element())
     then if (
       (normalize-space($node) = '') 
       and (not($node/@*[normalize-space(.)]))    
     )
          then (
            /* updated code here */
            if(($node/@*[normalize-space(.)])= '')  then ()
            else
              $node/@*[normalize-space(.)]

          )
          else element { node-name($node)
        }
       { $node/@*, local:remove-empty-elements($node/node())}
     else if ($node instance of document-node())
          then local:remove-empty-elements($node/node())
     else $node
};
1

1 Answers

1
votes

This part was causing the problem :

and (not(normalize-space($node/@*)))  

Call normalize-space() function in predicate for individual attribute instead of passing all attributes to the function at once, like so :

and (not($node/@*[normalize-space(.)]))  

update :

Regarding the question update, you can use the same predicate to filter the attributes so that you end up with non-empty attributes only. In the inner else block, set content of the returned element as follow :

else element {node-name($node)}
             { 
                $node/@*[normalize-space(.)], 
                local:remove-empty-elements($node/node())
             }