How to change namespace uri using xsl in soap requ

2019-08-01 00:58发布

I have this soap request :

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"         xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<soapenv:Header>
    <wsse:Security mustUnderstand="1">
        <wsse:UsernameToken>
            <wsse:Username>test</wsse:Username>
            <wsse:Password>test</wsse:Password>
        </wsse:UsernameToken>
    </wsse:Security>
</soapenv:Header>
<soapenv:Body>
  <hel:docTypeRef_tns_sayHello xmlns:hel="http://aaa/bbb.fr">
     <arg0>John</arg0>
     <arg1>111-222-333</arg1>
  </hel:docTypeRef_tns_sayHello>
</soapenv:Body>
</soapenv:Envelope>

I would like first to change the namespace uri appearing in the hel:docTypeRef_tns_sayHello element to something else (ex : http://test.fr). This namespace definition can appear in the hel:docTypeRef_tns_sayHello element as in the code above or in the root Envelope element, so i'd like to add this namespace definition only in the Envelope element

Then I'd like to modify the value of mustUnderstand attribute to 0.

The result should be of the form :

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"       xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-    1.0.xsd" 
**xmlns:hel="http://test.fr"**>
<soapenv:Header>
    <wsse:Security mustUnderstand="**0**">
        <wsse:UsernameToken>
            <wsse:Username>test</wsse:Username>
            <wsse:Password>test</wsse:Password>
        </wsse:UsernameToken>
    </wsse:Security>
</soapenv:Header>
<soapenv:Body>
  <hel:docTypeRef_tns_sayHello>
     <arg0>John</arg0>
     <arg1>111-222-333</arg1>
  </hel:docTypeRef_tns_sayHello>
</soapenv:Body>
</soapenv:Envelope>

Could somebody help me ? Thancks !

1条回答
做个烂人
2楼-- · 2019-08-01 01:40

I. Here is a generic, parameterized XSLT 1.0 solution:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pElemName" select="'hel:docTypeRef_tns_sayHello'"/>
 <xsl:param name="pOldNamespace" select="'http://aaa/bbb.fr'"/>
 <xsl:param name="pNewNamespace" select="'http://test.fr'"/>

 <xsl:variable name="vPrefix" select="substring-before($pElemName, ':')"/>

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

 <xsl:template match="*">
  <xsl:choose>
      <xsl:when test=
       "not(name() = $pElemName
          and
            namespace-uri() = $pOldNamespace
           )
       ">
        <xsl:call-template name="identity"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:element name="{name()}" namespace="{$pNewNamespace}">
         <xsl:copy-of select=
           "namespace::*
                 [not(name() = $vPrefix
                    and
                      . = $pOldNamespace
                      )
                ]"/>

         <xsl:apply-templates select="@*"/>

         <xsl:apply-templates select="node()" mode="removeNS"/>
        </xsl:element>
      </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template match="*" mode="removeNS">
  <xsl:choose>
   <xsl:when test=
     "not(starts-with(name(), $vPrefix)
        and
          namespace-uri() = $pOldNamespace
         )">
     <xsl:element name="{name()}" namespace="{namespace-uri()}">
         <xsl:copy-of select=
           "namespace::*
                 [not(name() = $vPrefix
                    and
                      . = $pOldNamespace
                      )
                ]"/>
       <xsl:apply-templates select="@*"/>
       <xsl:apply-templates select="node()" mode="removeNS"/>
     </xsl:element>
   </xsl:when>
  </xsl:choose>
 </xsl:template>

 <xsl:template match="@mustUnderstand">
  <xsl:attribute name="{name()}">0</xsl:attribute>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<soapenv:Envelope
  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <soapenv:Header>
        <wsse:Security mustUnderstand="1">
            <wsse:UsernameToken>
                <wsse:Username>test</wsse:Username>
                <wsse:Password>test</wsse:Password>
            </wsse:UsernameToken>
        </wsse:Security>
    </soapenv:Header>
    <soapenv:Body>
        <hel:docTypeRef_tns_sayHello xmlns:hel="http://aaa/bbb.fr">
            <arg0>John</arg0>
            <arg1>111-222-333</arg1>
        </hel:docTypeRef_tns_sayHello>
    </soapenv:Body>
</soapenv:Envelope>

the wanted, correct result is produced:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
 xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
   <soapenv:Header>
      <wsse:Security mustUnderstand="0">
         <wsse:UsernameToken>
            <wsse:Username>test</wsse:Username>
            <wsse:Password>test</wsse:Password>
         </wsse:UsernameToken>
      </wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
      <hel:docTypeRef_tns_sayHello xmlns:hel="http://test.fr">
         <arg0>John</arg0>
         <arg1>111-222-333</arg1>
      </hel:docTypeRef_tns_sayHello>
   </soapenv:Body>
</soapenv:Envelope>

Do note: In XSLT 1.0 it isn't possible without using an extension function (at least xxx:node-set())to dynamically add a new namespace (that doesn't exist already as a namespace node) to an element. In XSLT 2.0 the new xsl:namespace instruction can be used to dynamically construct a new namespace node from dynamic (statically unknown) parts.

Here is a small example how with XSLT 1.0 to add a new, non-existent at the start of the transformation namespace node:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/*">
  <xsl:variable name="vrtfDoc">
   <t xmlns:hel="http://test.fr"/>
  </xsl:variable>

  <xsl:variable name="vNS" select=
   "ext:node-set($vrtfDoc)/*/namespace::*
                               [name()='hel']"/>

  <xsl:element name="{name()}"
               namespace="{namespace-uri()}">
    <xsl:copy-of select="namespace::*"/>

    <xsl:copy-of select="$vNS"/>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on this XML document:

<t xmlns:someNS="some:NS"/>

it re-creates the top element t with all its namespace nodes and adds one new namespace node to it:

<t xmlns:someNS="some:NS" xmlns:hel="http://test.fr"/>

II. Here is a full, XSLT 2.0 solution:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pElemName" select="'hel:docTypeRef_tns_sayHello'"/>
 <xsl:param name="pOldNamespace" select="'http://aaa/bbb.fr'"/>
 <xsl:param name="pNewNamespace" select="'http://test.fr'"/>

 <xsl:variable name="vPrefix" select="substring-before($pElemName, ':')"/>

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

 <xsl:template match="/*">
  <xsl:element name="{name()}" namespace="{namespace-uri()}">
   <xsl:namespace name="{$vPrefix}" select="$pNewNamespace"/>

   <xsl:copy-of select=
       "namespace::*
             [not(name() = $vPrefix
                and
                  . = $pOldNamespace
                  )
              ]"/>

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

 <xsl:template match="*">
  <xsl:choose>
      <xsl:when test=
       "not(name() = $pElemName
          and
            namespace-uri() = $pOldNamespace
           )
       ">
        <xsl:call-template name="identity"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:element name="{name()}" namespace="{$pNewNamespace}">
         <xsl:copy-of select=
           "namespace::*
                 [not(name() = $vPrefix
                    and
                      . = $pOldNamespace
                      )
                ]"/>

         <xsl:apply-templates select="@*"/>

         <xsl:apply-templates select="node()" mode="removeNS"/>
        </xsl:element>
      </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template match="*" mode="removeNS">
  <xsl:choose>
   <xsl:when test=
     "not(starts-with(name(), $vPrefix)
        and
          namespace-uri() = $pOldNamespace
         )">
     <xsl:element name="{name()}" namespace="{namespace-uri()}">
         <xsl:copy-of select=
           "namespace::*
                 [not(name() = $vPrefix
                    and
                      . = $pOldNamespace
                      )
                ]"/>
       <xsl:apply-templates select="@*"/>
       <xsl:apply-templates select="node()" mode="removeNS"/>
     </xsl:element>
   </xsl:when>
  </xsl:choose>
 </xsl:template>

 <xsl:template match="@mustUnderstand">
  <xsl:attribute name="{name()}">0</xsl:attribute>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the same XML document (above), the wanted, correct result is produced:

<soapenv:Envelope xmlns:hel="http://test.fr"
                  xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
   <soapenv:Header>
      <wsse:Security mustUnderstand="0">
         <wsse:UsernameToken>
            <wsse:Username>test</wsse:Username>
            <wsse:Password>test</wsse:Password>
         </wsse:UsernameToken>
      </wsse:Security>
   </soapenv:Header>
   <soapenv:Body>
      <hel:docTypeRef_tns_sayHello>
         <arg0>John</arg0>
         <arg1>111-222-333</arg1>
      </hel:docTypeRef_tns_sayHello>
   </soapenv:Body>
</soapenv:Envelope>
查看更多
登录 后发表回答