0

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>
user2607367
  • 225
  • 4
  • 25

2 Answers2

1

If you want your output elements to be in a known namespace, you can place them there without checking if the source nodes has a default namespace or not. The following stylesheet:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.origoservices.com">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="*">
    <xsl:element name="{local-name()}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates select="node()"/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

will return the same result:

<?xml version="1.0" encoding="UTF-8"?>
<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>

regardless of whether the input is:

<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>

or:

<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>

Note, however, that this places all elements of the source document in the specified namespace. If your source XML has elements that are not in the default namespace, and you want to preserve this distinction, then it gets more complicated.


Edit:

In response to:

To achieve this, I should be keeping track of "for which requests the namespace declaration has been added explicitely"

I don't know how you intend to do that, when your output does not have a node that would keep this information. If it had, you could set its value to:

<xsl:value-of select="/*/namespace::*[not (name())]='http://www.origoservices.com'"/>

which would make it "true" when the root node of the input document declares a default http://www.origoservices.com namespace, "false" otherwise.

michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
  • Most important part of this question is how to check whether the default namespace declaration is present or not. If it is not present then only I have to add the decalaration. Your answer doesn't provide the solution to that. – user2607367 May 06 '15 at 14:52
  • Why is this important? How is the intended result different in the two cases? – michael.hor257k May 06 '15 at 14:55
  • actually I am trying to implement this in Datapower, within its request rule.When client is ending the xml without default namespace declaration, I should be adding it , so that backend can process the xml.(I know this is a weird requirement, but this has to be implemented anyway). Before sending the response back to the client, I should be removing the namespace declaration within Datapower response rule. To achieve this, I should be keeping track of "for which requests the namespace declaration has been added explicitely" – user2607367 May 06 '15 at 15:08
  • @user2607367 I have tried to answer this in an edit to my post, but I still don't see how you intend to use this in practice. – michael.hor257k May 06 '15 at 15:33
  • Thanks Michael! I apologies if I made it too complicated for you to understand. In simple terms, there can be multiple namespace declaration in the root elements, however, I would need to add the default namespace explicitly in the xml if it is not present.However, the answer provided by you is not able to achieve this. – user2607367 May 07 '15 at 08:51
  • The answer provided by me adds the default namespace explicitly whether it is present in the XML or not. This is much simpler than testing if it is present, then adding it if it's not, otherwise copying it it from the source. If you can point out a case where the two methods will produce a different result, then this will have some meaning. Otherwise it's a pointless exercise. – michael.hor257k May 07 '15 at 12:21
  • @micahel.hor257k I have added the stylesheet which is working as per my expectation. Any suggestions from you will be welcome. – user2607367 May 08 '15 at 11:36
  • @user2607367 Unless you can point out a case where my answer returns an incorrect result, I will not spend any more time on this. – michael.hor257k May 08 '15 at 12:04
  • @micahel.hor257k I have tried your stylesheet, against an xml which has other namespace declaration for ex. `xmlns:a=http://www.bbb.com` and the default namespace declaration is absent. When such xml applied to your stylesheet, it will add the default namespace declaration and remove the existing one. I don't need this to happen. I need the existing namespace to be present and the default namespace should be added.This is exactly where your styelesheet is failing. The stylesheet which I have added to the updated questions transforms the xml as per my requirement. – user2607367 May 08 '15 at 13:05
  • You need to edit your question and add this XML to the examples, along with the expected result thereof. Note that `xmlns:a=http://www.bbb.com` is not a *default* namespace declaration, so I am not sure what exactly you mean. – michael.hor257k May 08 '15 at 13:13
  • I have not said that `xmlns:a=http://www.bbb.com` is a default namespace declaration. It is off-course not. If my previous comment is carefully read, it mentions this namespace as **other** namespace.My previous comment also mentions that **default namespace declaration is absent** . Also, the first sample xml provided in the question has the namespace declaration which is not default, which covers this case. – user2607367 May 08 '15 at 13:44
0

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>
user2607367
  • 225
  • 4
  • 25