Guest
Guest

Reputation: 455

XSLT transformation stripping all namespaces from soap:env

I have this Input XML to which i need to apply the XSL and transform it to a another XML which is on higher version. Lets say V3. So input XML is on version V1.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:NS1="http://www.test1/Error/v1"
      xmlns:NS2="http://www.test1/Error/schema/SCRIPT"
      xmlns:tns="http://www.test1/webservice/Service/v1"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Body>
      <tns:Sample>
         <NS2:Message release="006" version="010">
            <NS2:Header>
              <NS2:TestMessage>1</NS2:TestMessage>
              </NS2:Header>
            <NS2:Body>
               <NS2:SampleTx>
                  <NS2:Element1>
                     <NS2:IdName>
                        <NS2:ID>
                           <NS2:IDValue>114</NS2:IDValue>
                         </NS2:ID>
                     </NS2:IdName>
                  </NS2:Element1>
                  </NS2:SampleTx>
            </NS2:Body>
         </NS2:Message>
      </tns:Sample>
   </soapenv:Body>
</soapenv:Envelope>

The XSL I am applying is

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:djh="http://www.test1/webservice/Service/v1"
    xmlns:NS1="http://www.test1/Error/v1"
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <xsl:output indent="yes"/>

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

    <xsl:template match="djh:*">
        <xsl:element name="{name()}" namespace="http://www.test1/webservice/Service/v3">
            <xsl:apply-templates select="@*|node()"/>
        </xsl:element>  
    </xsl:template>

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

</xsl:stylesheet>

and the output I get is

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <tns:sendCancelRx xmlns:tns="http://www.test1/webservice/Service/v3">
         <NS2:Message release="006" version="010"
               xmlns:NS2="http://www.ncpdp.org/schema/SCRIPT">
            <NS2:Header>
             <NS2:TestMessage>1</NS2:TestMessage>
             </NS2:Header>
            <NS2:Body>
               <NS2:SampleTx>
              <NS2:Element1>
                 <NS2:IdName>
                    <NS2:ID>
                       <NS2:IDValue>114</NS2:IDValue>
                   </NS2:ID>
                 </NS2:IdName>
              </NS2:Element1>
              </NS2:SampleTx>
            </NS2:Body>
         </NS2:Message>
      </tns:sendCancelRx>
   </soapenv:Body>
</soapenv:Envelope>

It strips all the namespace declaration from xmlns:NS1="http://www.test1/Error/v1" xmlns:NS2="http://www.test1/Error/schema/SCRIPT" xmlns:tns="http://www.test1/webservice/Service/v1" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

and the output I want is

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:NS1="http://www.test1/Error/v3"
      xmlns:NS2="http://www.test1/Error/schema/SCRIPT"
      xmlns:tns="http://www.test1/webservice/Service/v3"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Body>
      <tns:Test>
         <NS2:Message release="006" version="010">
            <NS2:Header>
               <NS2:TestMessage>1</NS2:TestMessage>
              </NS2:Header>
            <NS2:Body>
               <NS2:SampleTx>
          <NS2:Element1>
             <NS2:IdName>
                <NS2:ID>
                   <NS2:IDValue>114</NS2:IDValue>
               </NS2:ID>
             </NS2:IdName>
          </NS2:Element1>
          </NS2:SampleTx>
            </NS2:Body>
         </NS2:Message>
      </tns:Test>
   </soapenv:Body>
</soapenv:Envelope>

I would appreciate if someone could let me know what is missing from my XSL.

Upvotes: 4

Views: 1518

Answers (3)

Mads Hansen
Mads Hansen

Reputation: 66783

The following XSLT preserves all of the namespace declarations (used and unused) and changes the tns namespace-uri to http://www.test1/webservice/Service/v3:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:djh="http://www.test1/webservice/Service/v1"
    xmlns:tns="http://www.test1/webservice/Service/v3"
    xmlns:NS1="http://www.test1/Error/v1"
    xmlns:NS2="http://www.test1/Error/schema/SCRIPT"
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <xsl:output indent="yes"/>

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

    <xsl:template match="djh:*">
        <!--reconstruct an element using the new v3 namespace,
            but use the same namespace-prefix "tns"-->
        <xsl:element name="tns:{local-name()}" 
                     namespace="http://www.test1/webservice/Service/v3">
            <xsl:apply-templates select="@*|node()"/>
        </xsl:element>  
    </xsl:template>

    <xsl:template match="*">
        <xsl:element name="{name()}" namespace="{namespace-uri()}">    
            <!--copy all of the namespace declarations, 
                except for "tns" - which is v1 in the source XML -->
            <xsl:copy-of select=
                    "namespace::*
                    [not(name()='tns')]"/> 
            <!--copy the namespace declaration for "tns" from the XSLT,
                which is v3, and add it to the element-->
            <xsl:copy-of select=
                "document('')/*/namespace::*[name()='tns']"/>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

It produces the following output with Saxon 9.x (does not preserve unused namespaces with Xalan, but that apparently is due to XALANJ-1959):

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:NS1="http://www.test1/Error/v1"
                  xmlns:NS2="http://www.test1/Error/schema/SCRIPT"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xmlns:tns="http://www.test1/webservice/Service/v3">
    <soapenv:Body>
        <tns:Sample>
            <NS2:Message release="006" version="010">
                <NS2:Header>
                    <NS2:TestMessage>1</NS2:TestMessage>
                </NS2:Header>
                <NS2:Body>
                    <NS2:SampleTx>
                        <NS2:Element1>
                            <NS2:IdName>
                                <NS2:ID>
                                    <NS2:IDValue>114</NS2:IDValue>
                                </NS2:ID>
                            </NS2:IdName>
                        </NS2:Element1>
                    </NS2:SampleTx>
                </NS2:Body>
            </NS2:Message>
        </tns:Sample>
    </soapenv:Body>
</soapenv:Envelope>

Upvotes: 2

Michael Kay
Michael Kay

Reputation: 163458

The difference between xsl:element name="{name()}" and xsl:copy is that xsl:copy copies the namespace declarations, while xsl:element doesn't. So use xsl:copy if you want them retained.

Upvotes: 2

LarsH
LarsH

Reputation: 28004

Not sure why you say that it's stripping all those namespace prefix declarations. It strips those that aren't needed, but it leaves the declarations for NS2 and tns (and soapenv).

Having declarations for namespace prefixes that are unused accomplishes nothing. Why do you care? All the elements in your output XML are in the correct namespace. That's what matters to any downstream XML consumer.

(I'm assuming that the change of the namespace URI for NS2 is not part of the problem you're trying to solve.)

XSLT makes sure that each element in the output XML is in the proper namespace. Other than that, it doesn't offer fine control over where namespace prefixes are declared, because it doesn't affect the semantics of the output XML.

Upvotes: 2

Related Questions