Example of an XML Refactoring Operation

To demonstrate creating a custom operation, consider that we have a task where we 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, our task is to convert this attribute into an element with the same name and insert it as the first child of the image element.

Example: Custom XML Refactoring Operation

A new custom XML refactoring operation requires:

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 XSLT Script for Creating a Custom Operation to Convert an Attribute to an Element

The XSLT stylesheet 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.
  • Adds the new element as the first child of the parent element.
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="xs"
      xmlns:xr="http://www.oxygenxml.com/ns/xmlRefactoring"
      version="2.0">
        
      <xsl:import 
    href="http://www.oxygenxml.com/ns/xmlRefactoring/resources/commons.xsl"/>
        
      <xsl:param name="element_localName" as="xs:string" required="yes"/>
      <xsl:param name="element_namespace" as="xs:string" required="yes"/>
      <xsl:param name="attribute_localName" as="xs:string" required="yes"/>
      <xsl:param name="attribute_namespace" as="xs:string" required="yes"/>
      <xsl:param name="new_element_localName" as="xs:string" required="yes"/>
      <xsl:param name="new_element_namespace" as="xs:string" required="yes"/>
        
      <xsl:template match="node() | @*">
          <xsl:copy>
              <xsl:apply-templates select="node() | @*"/>
          </xsl:copy>
      </xsl:template>
      <xsl:template match="//*[xr:check-local-name($element_localName, ., true())
         and
            xr:check-namespace-uri($element_namespace, .)]">
            
          <xsl:variable name="attributeToConvert" 
             select="@*[xr:check-local-name($attribute_localName, ., true())
         and
            xr:check-namespace-uri($attribute_namespace, .)]"/>
            
          <xsl:choose>
              <xsl:when test="empty($attributeToConvert)">
                  <xsl:copy>
                      <xsl:apply-templates select="node() | @*"/>
                  </xsl:copy>
              </xsl:when>
          <xsl:otherwise>
              <xsl:copy>
                  <xsl:for-each select="@*[empty(. intersect $attributeToConvert)]">
                       <xsl:copy-of select="."/>                        
                  </xsl:for-each>
                    <!-- The new element namespace -->
                    <xsl:variable name="nsURI" as="xs:string">
                        <xsl:choose>
               <xsl:when test="$new_element_namespace eq $xr:NO-NAMESPACE">
                               <xsl:value-of select="''"/>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:value-of select="$new_element_namespace"/>
                            </xsl:otherwise>
                        </xsl:choose>
                     </xsl:variable>
              <xsl:element name="{$new_element_localName}" namespace="{$nsURI}">
                        <xsl:value-of select="$attributeToConvert"/>
                     </xsl:element>
                  <xsl:apply-templates select="node()"/>
             </xsl:copy>
           </xsl:otherwise>
         </xsl:choose>
      </xsl:template>
    </xsl:stylesheet>

Note

The XSLT stylesheet imports a module library that contains utility functions and variables. The location of this module is resolved via an XML catalog set in the XML Refactoring framework.

Example of an Operation Descriptor File for Creating a Custom Operation to Convert an Attribute to an Element

After you have developed the XQuery script or XSLT stylesheet, you have to create an XML Refactoring operation descriptor. 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>    
 <!-- For the XSLT stylesheet option uncomment the following line and comment
           the line referring the XQuery Update script -->
 <!-- <script type="XSLT" href="convert-attribute-to-element.xsl"/> --> 
 <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>

Note

If you are using an XSLT file, the line with the script element would look like this:
 <script type="XSLT" href="convert-attribute-to-element.xsl"/>

Results

After you have created these files, copy them into a folder scanned by Oxygen XML Editor plugin 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 our example of the custom operation to convert an attribute to an element:

Example: XML Refactoring Wizard for a Custom Operation

Was this helpful?