Search code examples
xqueryxquery-updatexquery-3.0

Add an attribute to an element in an existing XML using Xquery


I need to add an attribute to an element of my response XML using XQuery. Take the below XML as input,

<xyz:RootNode xmlns:abc="url1" xmlns:xyz="url2">
  <abc:OtherNodes/>
  <abc:messageHeader att1="val1" att2="val2">
       <abc:childNodes/>  
  </abc:messageHeader>
  <abc:OtherNodes/>
</xyz:RootNode>

Need an Xquery that add one more attribute newAtt with value newVal and give the result as,

<xyz:RootNode xmlns:abc="url1" xmlns:xyz="url2">
   <abc:OtherNodes/>
   <abc:messageHeader att1="val1" att2="val2" newAtt="newVal">
       <abc:childNodes/>  
  </abc:messageHeader>
  <abc:OtherNodes>
</xyz:RootNode>

Each time the number of attributes of message header may change. So the query should add a new attribute along with all the existing attributes and return the whole document.


Solution

  • Try the following:

    xquery version "3.0";
    
    module namespace foo="http://exist-db.org/apps/ns/foo";
    
    declare function foo:process-node($node as node()?, $model as map()) {
        if ($node) then 
        typeswitch($node) 
            case text() return $node
            case element(messageHeader) return foo:messageHeader($node, $model)
            default return element { $node/name() } 
                                   { $node/@*, foo:recurse($node, $model) }
    
        else () 
    };
    
    declare function foo:recurse($node as node()?, $model as map()) as item()* {
        if ($node) 
        then 
            for $cnode in $node/node() 
            return foo:process-node($cnode, $model) 
        else ()
    };
    
    declare function foo:messageHeader($node as node(), $model as map()) {
    element { $node/name() } 
            { $node/@*, 
              attribute { 'newAtt' } { 'newVal' },
              foo:recurse($node, $model)
            }
    };
    

    You then call foo:process-node on the RootNode