Edit online

XQuery Update Script for Creating a Custom Operation

To demonstrate creating a custom operation, suppose that you have a task where you need to convert an attribute into an element and insert it inside another element. A specific example would be if you have a project with a variety of <image> elements where a deprecated @alt attribute was used for the description and you want to convert all instances of that attribute into an element with the same name and insert it as the first child of the <image> element.

Thus, the task is to convert this attribute into an element with the same name and insert it as the first child of the image element.

Figure 1. Example: Custom XML Refactoring Operation

An XQuery Update script can be used to implement the new custom XML refactoring operation. The second requirement is an XML Refactoring operation descriptor file that contains the path to the XQuery Update script.

Restriction: There is a limitation to using an XQuery script in that comments or processing instructions that are in any node before or after the root element cannot be modified by an XML Refactoring operation. In other words, XML Refactoring operations can only be performed on comments or processing instructions that are inside the root element. However, as a work around to this limitation, you can use Saxon extension functions and the XSLT stylesheet method to implement the new custom XML refactoring operation.

Example of an XQuery Update Script for Creating a Custom Operation to Convert an Attribute to an Element

The XQuery Update script does the following:
  • Iterates over all elements from the document that have the specified local name and namespace.
  • Finds the attribute that will be converted to an element.
  • Computes the QName of the new element to be inserted and inserts it as the first child of the parent element.
(: 
    XQuery document used to implement 'Convert attribute to element'
      operation from XML Refactoring tool.
:)

declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:method   "xml";
declare option output:indent   "no"; 

(: Local name of the attribute's parent element. :)
declare variable $element_localName as xs:string external;

(: Namespace of the attribute's parent element. :)
declare variable $element_namespace as xs:string external;

(: The local name of the attribute to be converted :)
declare variable $attribute_localName as xs:string external;

(: The namespace of the attribute to be converted :)
declare variable $attribute_namespace as xs:string external;

(: Local name of the new element. :)
declare variable $new_element_localName as xs:string external;

(: Namespace of the new element. :)
declare variable $new_element_namespace as xs:string external;

(: Convert attribute to element:)
for $node in //*
(: Find the attribute to convert :)
let $attribute := 
    $node/@*[local-name() = $attribute_localName and
    ($attribute_namespace = '<ANY>' or $attribute_namespace = namespace-uri())]
    
(: Compute the prefix for the new element to insert :)
let $prefix := 
    for $p in in-scope-prefixes($node)
      where $new_element_namespace = namespace-uri-for-prefix($p, $node)
return $p

(: Compute the qname for the new element to insert :)    
let $new_element_qName :=
    if (empty($prefix) or $prefix[1] = '') then $new_element_localName
    else $prefix[1] || ':' || $new_element_localName
    
 where ('<ANY>' = $element_localName or local-name($node) = $element_localName)
   and 
 ($element_namespace = '<ANY>' or $element_namespace = namespace-uri($node))
        
  return 
    if (exists($attribute)) then
      (insert node element {QName($new_element_namespace, $new_element_qName)} 
      {string($attribute)} as first into $node,
      delete node $attribute)
      else ()

Example of an Operation Descriptor File That References the XQuery Script for Creating a Custom Operation to Convert an Attribute to an Element

After you have developed the XQuery script (for example, named convert-attribute-to-element.xq), you have to create an XML Refactoring operation descriptor (for example, named convert-attribute-to-element.xml) that references the stylesheet and provides descriptions and possible values for its parameters. This descriptor is used by the application to load the operation details such as name, description, or parameters.

<?xml version="1.0" encoding="UTF-8"?>

<refactoringOperationDescriptor 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://www.oxygenxml.com/ns/xmlRefactoring" 
 id="convert-attribute-to-element" 
 name="Convert attribute to element">
 <description>Converts the specified attribute to an element. 
           The new element will be inserted as first child of the attribute's
           parent element.</description>    
 <script type="XQUERY_UPDATE" href="convert-attribute-to-element.xq"/>
  <parameters>
   <description>Specify the attribute to be converted to element.</description>
    <section label="Parent element">
     <elementParameter id="elemID">
      <localName label="Name" name="element_localName" allowsAny="true">
       <description>Local name of the parent element.</description>            
        </localName>
       <namespace label="Namespace" name="element_namespace" allowsAny="true">
         <description>Local name of the parent element</description>            
       </namespace>        
     </elementParameter>
    </section>
    <section label="Attribute">
     <attributeParameter dependsOn="elemID">
      <localName label="Name" name="attribute_localName">
       <description>Name of the attribute to be converted.</description>
      </localName>
     <namespace label="Namespace" name="attribute_namespace" allowsAny="true">
       <description>Namespace of the attribute to be converted.</description>
     </namespace>        
     </attributeParameter>
    </section>
    <section label="New element">
        <elementParameter>
           <localName label="Name" name="new_element_localName">
               <description>The name of the new element.</description>
           </localName>
           <namespace label="Namespace" name="new_element_namespace">
               <description>The namespace of the new element.</description>
           </namespace>        
        </elementParameter>
    </section>
  </parameters>
</refactoringOperationDescriptor>

Results

After you have created these files, copy them into a folder scanned by Oxygen XML Editor when it loads the custom operation. When the XML Refactoring tool is started again, you will see the created operation.

Since various parameters can be specified, this custom operation can also be used for other similar tasks. The following image shows the parameters that can be specified in the example of the custom operation to convert an attribute to an element:

Figure 2. Example: XML Refactoring Wizard for a Custom Operation

Debugging XQuery Refactoring Operations

You can use the XQuery trace() function to generate debugging information messages in the application's Results view.

The Saxon processor used for XQuery update processing also supports the EXPath extensions, which also allow you to write debugging information to an output file.