Search code examples
xsltxslt-1.0exsltxslt-grouping

replace namespace uri using XSLT in SOAP message


i have an input soap message, trying to replace part of namespace uri with a different string. I am able to replace entire URI with a different URI, but not able to modify the existing URI. I need to look for 'OLDSTRING' and replace with 'NEWSTRING'. The string VARIABLESTRING varies in in every input xml, so I should keep as it is in the output xml

Input XML:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:schemas-OLDSTRING-com:VARIABLESTRING">
       <soapenv:Header/>
       <soapenv:Body>
          <requestMessage xmlns="urn:schemas-OLDSTRING-com:VARIABLESTRING">
            <merchantID>TESTID</merchantID>   
          </requestMessage>
       </soapenv:Body>
    </soapenv:Envelope>

OUTPUT XML:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:schemas-NEWSTRING-com:transaction-data-1.69">
   <soapenv:Header/>
   <soapenv:Body>
      <requestMessage xmlns="urn:schemas-NEWSTRING-com:VARIABLESTRING">
        <merchantID>TESTID</merchantID>   
      </requestMessage>
   </soapenv:Body>
</soapenv:Envelope>

I have tried the following XSL, and able to change the namespace URI, but i want to replace only 'OLDSTRING' with 'NEWSTRING' and keep remaining string as it is.

XSLT:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
       <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="@*|*|text()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="//*[namespace-uri()='urn:schemas-OLDSTRING-com:VARIABLESTRING']">
        <xsl:element name="{local-name()}" namespace="urn:schemas-NEWSTRING-com:VARIABLESTRING" >
            <xsl:apply-templates select="@*|*|text()" />
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

Solution

  • This kind of processing is easy in XSLT 2.0, but quite tricky in XSLT 1.0:

    <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:strip-space elements="*"/>
    
     <xsl:param name="pTarget" select="'OLDSTRING'"/>
     <xsl:param name="pRepl" select="'NEWSTRING'"/>
    
     <xsl:template match="node()|@*">
      <xsl:copy>
       <xsl:apply-templates select="node()|@*"/>
      </xsl:copy>
     </xsl:template>
    
     <xsl:template match=
      "*[starts-with(namespace-uri(), 'urn:schemas')
       or
         namespace::*[starts-with(., 'urn:schemas')]
        ]">
    
      <xsl:variable name="vNS" select="namespace-uri()"/>
    
      <xsl:variable name="vNewNS">
        <xsl:choose>
          <xsl:when test="not(contains($vNS, $pTarget))">
            <xsl:value-of select="$vNS"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="substring-before($vNS, $pTarget)"/>
            <xsl:value-of select="$pRepl"/>
            <xsl:value-of select="substring-after($vNS, $pTarget)"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:variable>
    
      <xsl:element name="{name()}" namespace="{$vNewNS}">
       <xsl:copy-of select=
         "namespace::*
            [not(. = $vNS
                or
                 starts-with(., 'urn:schemas')
               and
                 contains(., $pTarget)
                 )
            ]"/>
    
       <xsl:for-each select=
         "namespace::*
            [starts-with(., 'urn:schemas')
           and
             contains(., $pTarget)
            ]">
         <xsl:variable name="vNewNSUri" select=
         "concat(substring-before(., $pTarget),
                 $pRepl,
                 substring-after(., $pTarget)
                 )
         "/>
    
         <xsl:variable name="vPrefix" select="name()"/>
    
         <xsl:variable name="vPref">
           <xsl:if test="$vPrefix">
             <xsl:value-of select="concat($vPrefix, ':')"/>
           </xsl:if>
         </xsl:variable>
    
         <xsl:variable name="vrtfDoc">
          <xsl:element name="{$vPref}dummy"
                       namespace="{$vNewNSUri}"/>
         </xsl:variable>
    
         <xsl:copy-of select=
         "ext:node-set($vrtfDoc)/*/namespace::*[. = $vNewNSUri]"/>
       </xsl:for-each>
    
       <xsl:apply-templates select="node()|@*"/>
      </xsl:element>
     </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:urn="urn:schemas-OLDSTRING-com:VARIABLESTRING">
        <soapenv:Header/>
        <soapenv:Body>
            <requestMessage xmlns="urn:schemas-OLDSTRING-com:VARIABLESTRING">
                <merchantID>TESTID</merchantID>
            </requestMessage>
        </soapenv:Body>
    </soapenv:Envelope>
    

    the wanted, correct result is produced:

    <soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:urn="urn:schemas-NEWSTRING-com:VARIABLESTRING">
      <soapenv:Header />
      <soapenv:Body>
        <requestMessage xmlns="urn:schemas-NEWSTRING-com:VARIABLESTRING">
          <merchantID>TESTID</merchantID>
        </requestMessage>
      </soapenv:Body>
    </soapenv:Envelope>