Search code examples
xmlxsltnamespacesxml-namespacesibm-datapower

Adding default namespace conditionally using xslt


Requirement:

  • Check whether a default namespace declaration xmlns="http://www.origoservices.com present in the request xml.

  • If not, add the default namespace declaration.

Sample Request xml 1:

<message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<m_control>
        <control_timestamp>2014-11-05T09:30:38.308</control_timestamp>
        <retry_number>0</retry_number>
        <expected_response_type>synchronous</expected_response_type>
        <responder_id>Exchange Life 1</responder_id>
</m_control>
</message>

Expected Output:

   <message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.origoservices.com">
<m_control>
         <control_timestamp>2014-11-05T09:30:38.308</control_timestamp>
          <retry_number>0</retry_number>
    <expected_response_type>synchronous</expected_response_type>
    <responder_id>Exchange Life 1</responder_id>
</m_control>
</message>

Sample request xml 2

  <message>
<m_control>
    <control_timestamp>2014-11-05T09:30:38.308</control_timestamp>
        <retry_number>0</retry_number>
    <expected_response_type>synchronous</expected_response_type>
    <responder_id>Exchange Life 1</responder_id>
</m_control>
</message>

Expected Output

   <message xmlns="http://www.origoservices.com">
<m_control>
    <control_timestamp>2014-11-05T09:30:38.308</control_timestamp>
        <retry_number>0</retry_number>
    <expected_response_type>synchronous</expected_response_type>
    <responder_id>Exchange Life 1</responder_id>
</m_control>
</message>

Sample Request xml 3

<message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.origoservices.com">
<m_control>
    <control_timestamp>2014-11-05T09:30:38.308</control_timestamp>
        <retry_number>0</retry_number>
    <expected_response_type>synchronous</expected_response_type>
    <responder_id>Exchange Life 1</responder_id>
</m_control>
</message>

Expected output : This should be same as input as it already has default namespace declared.

<message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.origoservices.com">
<m_control>
    <control_timestamp>2014-11-05T09:30:38.308</control_timestamp>
        <retry_number>0</retry_number>
    <expected_response_type>synchronous</expected_response_type>
    <responder_id>Exchange Life 1</responder_id>
</m_control>
</message>

I have tried below xslt, but not sure how to add the condition to check the existence of the default namespace declaration in the request xml.

   <xsl:stylesheet version="1.0"     xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dp="http://www.datapower.com/extensions" xmlns="http://www.origoservices.com" extension-element-prefixes="dp" exclude-result-prefixes="dp">
   <xsl:output method="xml"/>
   <xsl:output omit-xml-declaration="yes" indent="yes"/>

   <!-- Below statements will copy all the elements and attributes from source to destination, normally this will copy over the element and attributes tags to destination-->

   <xsl:template match="node()|@*">
   <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
   </xsl:copy>
   </xsl:template>

  <xsl:template match="/*">
  <message xmlns="http://www.origoservices.com">
  <!--below statement will copy all the existing namespace declaration from source to destination-->
  <xsl:copy-of select="namespace::*" />
  <!--below statement will copy all the elements and attributes within the message root element to the resulting doc -->
  <xsl:apply-templates select="@*|node()" />
  </message>
</xsl:template>

UPDATE The below xslt works the way I wanted. However, I am sure there is lot of scope of improvement here.I would like experts to review this and suggest the improvements, and any loop holes, in case if they are present.

  <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dp="http://www.datapower.com/extensions" xmlns:regexp="http://exslt.org/regular-expressions" extension-element-prefixes="dp" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="dp regexp exsl">

  <xsl:variable name="origo-svc-ns" select="'http://www.origoservices.com'"/>

  <xsl:template match="/*[local-name()='message']">
 <!--xsl:template match="/message"-->
         <!--xsl:variable name="name" select="name(/*[1])"/-->
         <!--xsl:variable name="namespace-in" select="namespace-uri(/*[1])"/-->

         <xsl:variable name="name" select="name()"/>

         <!--Variable "namespace-in" will contain the namespace uri of the namspace of which message element is a part-->
         <!--As message element is a part of namespace having uri "http://www.origoservices.com", this value will be assigned to the variable -->
         <xsl:variable name="namespace-in" select="namespace-uri()"/>


                     <!--Set Variable which stores the default namespace URI. This step will also set a context variable  "AddNamespace"  with value "Y" -->       
         <xsl:variable name="namespace">
                  <xsl:choose>
                      <xsl:when test="$namespace-in = ''">
                              <xsl:value-of select="$origo-svc-ns"/>
                              <dp:set-variable name="'var://context/FL/AddNamspace'" value="Y"/>
                      </xsl:when>
                     <xsl:otherwise>
                              <xsl:value-of select="$namespace-in"/>
                     </xsl:otherwise>
                </xsl:choose>
      </xsl:variable>


       <!-- - In below statement, {$namespace} will copy over the default namespace declarartion to the destination.
            - copy-of select statement will copy over all the namespace declaration in the source xml 
            - apply-template will copy over evrything else from the source to destination
            - xsl:element will create an element node (in this case <message> ) in the destination document.      
      -->

    <xsl:element name="{$name}" namespace="{$namespace}">
  <xsl:copy-of select="namespace::*"/>
  <xsl:apply-templates select="@*|node()|comment()|processing-instruction()|text()">
    <xsl:with-param name="ns-uri" select="$namespace"/>
  </xsl:apply-templates>
  </xsl:element>

  </xsl:template>

  <!--Above template only copy over the values of element,attributes,etc to the destination, below template copies the names of elements (only nodes) to the destination-->

   <xsl:template match="node()">
     <xsl:param name="ns-uri"/>
       <xsl:element name="{local-name()}" namespace="{$ns-uri}">
         <xsl:apply-templates select="@*|node()|comment()|processing-instruction()|text()">
      <xsl:with-param name="ns-uri" select="$ns-uri"/>
       </xsl:apply-templates>
   </xsl:element>
    </xsl:template>

     <xsl:template match="@*|comment()|processing-instruction()|text()">
         <xsl:param name="ns-uri"/>
           <xsl:copy>
            <xsl:apply-templates select="@*|node()|comment()|processing-instruction()|text()">
          <xsl:with-param name="ns-uri" select="$ns-uri"/>
         </xsl:apply-templates>
          </xsl:copy>
     </xsl:template>

   </xsl:stylesheet>

Solution

  • The below xslt works fine for me.

      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dp="http://www.datapower.com/extensions" xmlns:regexp="http://exslt.org/regular-expressions" extension-element-prefixes="dp" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="dp regexp exsl">
    
         <xsl:variable name="origo-svc-ns" select="'http://www.origoservices.com'"/>
    
          <xsl:template match="/*[local-name()='message']">
          <!--xsl:template match="/message"-->
         <!--xsl:variable name="name" select="name(/*[1])"/-->
         <!--xsl:variable name="namespace-in" select="namespace-uri(/*[1])"/-->
    
          <xsl:variable name="name" select="name()"/>
    
         <!--Variable "namespace-in" will contain the namespace uri of the namspace of which message element is a part-->
         <!--As message element is a part of namespace having uri "http://www.origoservices.com", this value will be assigned to the variable -->
          <xsl:variable name="namespace-in" select="namespace-uri()"/>
    
    
                     <!--Set Variable which stores the default namespace URI. This step will also set a context variable  "AddNamespace"  with value "Y" -->       
           <xsl:variable name="namespace">
                  <xsl:choose>
                      <xsl:when test="$namespace-in = ''">
                              <xsl:value-of select="$origo-svc-ns"/>
                              <dp:set-variable name="'var://context/FL/AddNamspace'" value="Y"/>
                      </xsl:when>
                     <xsl:otherwise>
                              <xsl:value-of select="$namespace-in"/>
                     </xsl:otherwise>
                </xsl:choose>
         </xsl:variable>
    
    
       <!-- - In below statement, {$namespace} will copy over the default namespace declarartion to the destination.
            - copy-of select statement will copy over all the namespace declaration in the source xml 
            - apply-template will copy over evrything else from the source to destination
            - xsl:element will create an element node (in this case <message> ) in the destination document.      
      -->
    
       <xsl:element name="{$name}" namespace="{$namespace}">
       <xsl:copy-of select="namespace::*"/>
       <xsl:apply-templates select="@*|node()|comment()|processing-instruction()|text()">
       <xsl:with-param name="ns-uri" select="$namespace"/>
       </xsl:apply-templates>
       </xsl:element>
    
       </xsl:template>
    
       <!--Above template only copy over the values of element,attributes,etc to the destination, below template copies the names of elements (only nodes) to the destination-->
    
       <xsl:template match="node()">
         <xsl:param name="ns-uri"/>
           <xsl:element name="{local-name()}" namespace="{$ns-uri}">
             <xsl:apply-templates select="@*|node()|comment()|processing-instruction()|text()">
          <xsl:with-param name="ns-uri" select="$ns-uri"/>
         </xsl:apply-templates>
         </xsl:element>
         </xsl:template>
    
         <xsl:template match="@*|comment()|processing-instruction()|text()">
         <xsl:param name="ns-uri"/>
           <xsl:copy>
            <xsl:apply-templates select="@*|node()|comment()|processing-instruction()|text()">
          <xsl:with-param name="ns-uri" select="$ns-uri"/>
         </xsl:apply-templates>
          </xsl:copy>
         </xsl:template>
    
        </xsl:stylesheet>