How to do a deep copy of XML, and make changes using XQuery
Since XQuery is a functional language and you can’t change the value of a variable (you can only redeclare the variable to have a new value), if you want to make a change to an XML tree, you have to actually create a new copy of the XML with your changes in it. You can’t change the original XML. There are some utility functions that make the process fairly easy, like in-mem-update and node-replace, but they may not be the best options in some cases.
Below is a simple way to make a deep copy of an element and introduce change to it, or its immediate children. If you want to make changes deeper than 1 level, or want to make changes based on some rules, I’d recommend use the dispatch-passthru method or using XSLT. But if you just want to add or remove an element or attribute, consider the following:
Adding an element:
xquery version '1.0-ml';
declare namespace ryan = "http://ryan";
declare namespace bob = "http://bob.com/xyz";
let $example :=
<ryan:sky time="now">
Clear
<cloudy/>
<bob:forecast>Snow</bob:forecast>
</ryan:sky>
return
element { fn:QName(fn:namespace-uri($example), fn:name($example)) }
{ $example/(@*|node()), <speak>Hello</speak> }
=> <ryan:sky time="now" xmlns:ryan="http://ryan">
Clear
<cloudy/><bob:forecast xmlns:bob="http://bob.com/xyz">Snow</bob:forecast><speak>Hello</speak></ryan:sky>
Excluding an element (removing it) using “except”:
xquery version '1.0-ml';
declare namespace ryan = "http://ryan";
declare namespace bob = "http://bob.com/xyz";
let $example :=
<ryan:sky time="now">
Clear
<cloudy/>
<bob:forecast>Snow</bob:forecast>
</ryan:sky>
return
element { fn:QName(fn:namespace-uri($example), fn:name($example)) }
{ $example/(@*|node()) except $example//cloudy }
=> <ryan:sky time="now" xmlns:ryan="http://ryan">
Clear
<bob:forecast xmlns:bob="http://bob.com/xyz">Snow</bob:forecast></ryan:sky>
Excluding an element (removing it) using the element name:
xquery version '1.0-ml';
declare namespace ryan = "http://ryan";
declare namespace bob = "http://bob.com/xyz";
let $example :=
<ryan:sky time="now">
Clear
<cloudy/>
<bob:forecast>Snow</bob:forecast>
</ryan:sky>
return
element { fn:QName(fn:namespace-uri($example), fn:name($example)) }
{ $example/(@*|node())[fn:not(fn:local-name(.) eq "cloudy")] }
=> <ryan:sky time="now" xmlns:ryan="http://ryan">
Clear
<bob:forecast xmlns:bob="http://bob.com/xyz">Snow</bob:forecast></ryan:sky>
Excluding an attribute (removing it) using “except”:
xquery version '1.0-ml';
declare namespace ryan = "http://ryan";
declare namespace bob = "http://bob.com/xyz";
let $example :=
<ryan:sky time="now">
Clear
<cloudy/>
<bob:forecast>Snow</bob:forecast>
</ryan:sky>
return
element { fn:QName(fn:namespace-uri($example), fn:name($example)) }
{ $example/(@*|node()) except $example/@time }
=> <ryan:sky xmlns:ryan="http://ryan">
Clear
<cloudy/><bob:forecast xmlns:bob="http://bob.com/xyz">Snow</bob:forecast></ryan:sky>
Adding an attribute using attribute constructor (remember that attributes must come after the owner node or other attributes):
xquery version '1.0-ml';
declare namespace ryan = "http://ryan";
declare namespace bob = "http://bob.com/xyz";
let $example :=
<ryan:sky time="now">
Clear
<cloudy/>
<bob:forecast>Snow</bob:forecast>
</ryan:sky>
return
element { fn:QName(fn:namespace-uri($example), fn:name($example)) }
{ attribute { "quality" } { "good" }, $example/(@*|node()) }
=> <ryan:sky quality="good" time="now" xmlns:ryan="http://ryan">
Clear
<cloudy/><bob:forecast xmlns:bob="http://bob.com/xyz">Snow</bob:forecast></ryan:sky>
..and so forth

