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.
A new custom XML refactoring operation requires:
- An XQuery Update script or XSLT stylesheet.
- An XML Refactoring operation descriptor file that contains the path to the XQuery Update script or XSLT stylesheet.
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
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
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 Developer 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: