duncdrum
duncdrum

Reputation: 733

remove empty attributes xquery3.0

here is a sample of my xml.

 let $test :=   
    <root>
        <a z="">stuff</a>
        <b z="12" y="">more stuff</b>
        <c>stuff</c>
        <d z = " " y="0" x ="lkj">stuff goes wild</d>
    </root>

i would like to remove empty attributes using query to get this :

<root>
    <a>stuff</a>
    <b z="12">more stuff</b>
    <c>stuff</c>
    <d y="0" x ="lkj">stuff goes wild</d>
</root>  

I've gotten this far with my query, but i cannot get it to only remove the empty attribute, instead of removing all attributes if there is any empty one inside the element.

declare function local:sanitize ($nodes as node()*) {
for $n in $nodes 
return typeswitch($n)
    case element() return 
        if ($n/@*[normalize-space()='']) then  (element{node-name($n)} {($n/@*[.!=''], local:sanitize($n/node()))})
        else (element {node-name($n)} {($n/@*, local:sanitize($n/node()))})
default return ($n)
};

The function needs to be performant hence my desire to use typeswitch. I feel i m close but the last step seems to elude me. ie. z = " " doesn't get caught. Thanks for the help.

Upvotes: 1

Views: 344

Answers (1)

Jens Erat
Jens Erat

Reputation: 38682

The problem with your code is that when recreating the elements, you're checking for completely empty attributes, not empty attributes after whitespace normalization. Add this, and you're fine.

if ($n/@*[normalize-space()='']) then  (element{node-name($n)} {($n/@*[normalize-space(.)!=''], local:sanitize($n/node()))})

I simplified the pattern and distinguish between attributes, elements and everything else. Filter for empty attributes, recreate elements and just return anything else. The resulting function is much easier to read and understand, and produces the correct output:

declare function local:sanitize ($nodes as node()*) {
for $node in $nodes 
return typeswitch($node)
  (: filter empty attributes :)
  case attribute() return $node[normalize-space(.)]
  (: recreate elements :)
  case element() return 
    element { node-name($node) } {
      (: Sanitize all children :)
      for $child in $node/(attribute(), node())
      return local:sanitize($child)
    }
  (: neither element nor attribute :)
  default return $node
};

Upvotes: 2

Related Questions