To process all nodes in a tree we will start with recursive function called the identity transform. This function copies the source tree into the output tree without change. We begin with this process and then add some exception processing for each filter.

(: return a deep copy of the element and all sub elements :)
declare function local:copy($element as element()) as element() {
element {node-name($element)}
{$element/@*,
for $child in $element/node()
return
if ($child instance of element())
then local:copy($child)
else $child
}
};

This function uses an XQuery construct called computed element constructor to construct an element. The format of the element constructor is the following:

element {ELEMENT-NAME} {ELEMENT-VALUE}

In the above case ELEMENT-VALUE is another query that finds all the child elements of the current node. The for loop selects all nodes of the current element and does the following pseudo-code:

if the child is another element ''(this uses the "instance of" instruction)''
then copy the child ''(recursively)''
else return the child ''(we have a leaf element of the tree)''

If you understand this basic structure of this algorithm you can now modify it to filter out only the elements you want. You just start with this template and modify various sections.

Note that you can also achieve this function by using the typeswitch operator:

declare function local:rename-elements($input as node(), $map as node()) as node() {
let $current-element-name := name($input)
return
(: we create a new element with a name and a content :)
element
{ (: the new name is created here :)
if (local:element-in-map($current-element-name, $map) )
then local:new-name($current-element-name, $map)
else node-name($input)
}
{ (: the element content is created here :)
$input/@*, (: copy all attributes :)
for $child in $input/node()
return
if ($child instance of element())
then local:rename-elements($child, $map)
else $child
}
};
(: return true() if an element is in the from of a rename map :)
declare function local:element-in-map($element-name as xs:string, $map as node()) as xs:boolean {
exists($map/map[./from = $element-name])
};
(: return the new element name of an element in a rename map :)
declare function local:new-name($element-name as xs:string, $map as node()) as xs:string {
$map/map[./from = $element-name]/to
};

Here is a function that will add a namespace to the root element in an XML document. Note that is uses an element constructor with two parameters. The first parameter is the element name and the second is the element content. The element name is created using the QName() function and the element content is created using {$in/@*, $in/node()}, which will add attributes and all child nodes.

Below two functions will remove any namespace from a node, nnsc stands for no-namespace-copy. The first one performs much faster: From my limited understanding it jumps attributes quicker. The other one still here, something tricky might be hidden there.

You can integrate several functions for altering the node tree in one function. In the following function a number of common operations on elements are facilitated.

The parameters passed are 1) the node tree to be operated on, 2) any new item(s) to be inserted, 3) the action to be performed, 4) the name(s) of the element(s) targeted by the action.

The function can insert one or more elements supplied as a parameter in a certain position relative to (before or after or as the first or last child of) target elements in the node tree.

One or more elements can be inserted in the same position as the target element(s), i.e. they can substitute for them.

If the action is 'remove', the target element(s) are removed. If the action is 'remove-if-empty', the target element(s) are removed if they have no (normalized) string value. If the action is 'substitute-children-for-parent', the target element(s) are substituted by their child element(s). (In the last three cases the new content parameter is not consulted and should, for clarity, be the empty sequence).

If the action to be taken is 'change-name', the name of the element is changed to the first item of the new content.

If the action to be taken is 'substitute-content', any children of the target element(s) are substituted with the new content.

Note that context-free functions, for instance current-date(), can be passed as new content.

Note that if the target element is 'p', the $new-node will be inserted in relation to every element named 'p'.

You can fill in any functions you need and delete those that you do not need.

This following function facilitates several operations on attributes. This is more complicated than working with elements, for element names have to be considered as well.

The parameters passed are 1) the node tree to be operated on, 2) a new attribute name, 3) the new attribute contents, 4) the action to be performed, 5) the name(s) of the element(s) targeted by the action, 6) the name(s) of the attribute(s) targeted by the action.

By just using the action parameter, you can remove all empty-attributes.

If you wish to remove all named attributes, you need to supply the name of the attribute to be removed.

If you wish to change all values of named attributes, you need to supply the new value as well.

If you wish to attach an attribute, with name and value, to a specific element, you need to supply parameters for the element the attribute is to be attached to, the name of the attribute, and the value of the attribute, as well as the action.

If you wish to remove an attribute from a specific element, you need to supply parameters for the element the attribute is to be removed from, the name of the attribute, as well as the action.

If you wish to change the name of an attribute attached to a specific element, you need to supply parameters for the element the attribute is attached to, the name the attribute has, the new the attribute is to have, as well as the action.