Adding default namespace conditionally using xslt

流过昼夜 提交于 2019-12-08 09:09:22

问题


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>

回答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.




回答2:


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>


来源:https://stackoverflow.com/questions/30079553/adding-default-namespace-conditionally-using-xslt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!