Najma
Najma

Reputation: 29

XSLT -Need to copy multiple namespaces

I have the following XML. I have to include soap body under soap envelope.

<headers>
    <soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:def="http://lmn" xmlns:abc="http://xyz"><soapenv:Header><abc:Security>    <abc:UsernameToken>
    <abc:Username>abc</abc:Username>
                        <abc:Password>pwd</abc:Password>
                    </abc:UsernameToken>
            </abc:Security>
        </soapenv:Header> 
    </soapenv:Envelope>
 </headers>

My output should be like

     <soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:def="http://lmn" xmlns:abc="http://xyz">
         <soapenv:Header>
            <abc:Security>
                    <abc:UsernameToken>
                        <abc:Username>abc</abc:Username>
                        <abc:Password>pwd</abc:Password>
                    </abc:UsernameToken>
            </abc:Security>
        </soapenv:Header> 
        <soapenv:Body><a><b></b><c></c></a></soapenv:Body>
     </soapenv:Envelope>

Following is my XSL.

 <?xml version="1.0" encoding="UTF-8"?>
 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl" xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:soap="http://www.w3.org/2003/05/soap-envelope" exclude-result-prefixes="xd soap soapenv">
 <xsl:template match="/">
         <xsl:variable name="envNode"><xsl:copy-of select="name(headers/*)"/></xsl:variable>
         <xsl:choose>
             <xsl:when test="contains($envNode,'soapenv')">
                 <xsl:element name="{name(headers/*)}">                   
                     <xsl:copy-of select="headers/soapenv:Envelope/*"/>
                     <xsl:element name="soapenv:Body">
                         <a><b></b><c></c></a>
                     </xsl:element>
                 </xsl:element>
             </xsl:when>
             <xsl:otherwise>
                <xsl:element name="{name(soapOptions/headers/*)}">                   
            <xsl:copy-of select="soapOptions/headers/soap:Envelope/*"/>
            <xsl:element name="soap:Body">
                 <a><b></b><c></c></a>
            </xsl:element>
             </xsl:element>
            </xsl:otherwise>
         </xsl:choose>
  </xsl:template> 

I am getting the following output.

  <soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
    <soapenv:Header xmlns:def="http://lmn" xmlns:abc="http://xyz"">
            <abc:Security>
                    <abc:UsernameToken>
                        <abc:Username>abc</abc:Username>
                        <abc:Password>pwd</abc:Password>
                    </abc:UsernameToken>
            </abc:Security>
        </soapenv:Header>
        <soapenv:Body><a><b/><c/></a></soapenv:Body>
 </soapenv:Envelope>

I want to copy all the namespaces which is there in soapenv:Envelope node to the same node. But it is getting copied to soapenv:Header. I tried many options namespace options along with namespace in element name in XSL. Can anyone help me out in this.

Thanks in advance, Najma

Upvotes: 2

Views: 1723

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243449

Use what is probably the shortest, simplest and most standardized solution:

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

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

 <xsl:template match="soapenv:Envelope">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
   <soapenv:Body><a><b/><c/></a></soapenv:Body>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document:

<headers>
    <soapenv:Envelope
      xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"
      xmlns:def="http://lmn" xmlns:abc="http://xyz">
        <soapenv:Header>
            <abc:Security>
                <abc:UsernameToken>
                    <abc:Username>abc</abc:Username>
                    <abc:Password>pwd</abc:Password>
                </abc:UsernameToken>
            </abc:Security>
        </soapenv:Header>
    </soapenv:Envelope>
</headers>

the wanted, correct result is produced:

<headers>
   <soapenv:Envelope
    xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope"
    xmlns:def="http://lmn" xmlns:abc="http://xyz">
      <soapenv:Header>
         <abc:Security>
            <abc:UsernameToken>
               <abc:Username>abc</abc:Username>
               <abc:Password>pwd</abc:Password>
            </abc:UsernameToken>
         </abc:Security>
      </soapenv:Header>
      <soapenv:Body>
         <a>
            <b/>
            <c/>
         </a>
      </soapenv:Body>
   </soapenv:Envelope>
</headers>

Explanation:

  1. The identity rule/template copies every node "as-is".

  2. There is just one template overriding the identity rule. It matches soapenv:Envelope. The processing is very similar to that of the identity template (the matched element is "shallow-copied and all its descendants are copied within its body), but in addition, the new soapenv:Body is also output.

Upvotes: 1

Emiliano Poggi
Emiliano Poggi

Reputation: 24826

Add under the element declaration:

 <xsl:copy-of select="headers/*/namespace::*"/>

This will copy the wanted namespaces in the correct element, thus preventing the XSLT processor to declare them in other places.


XSLT verification:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">

    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="/">
        <xsl:element name="{name(headers/*)}">
            <xsl:copy-of select="headers/*/namespace::*"/>
            <xsl:copy-of select="headers/soapenv:Envelope/*"/>
            <xsl:element name="soapenv:Body">
                <a><b></b><c></c></a>
            </xsl:element>
        </xsl:element>

    </xsl:template>

</xsl:stylesheet>

produces:

<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" xmlns:def="http://lmn" xmlns:abc="http://xyz">
   <soapenv:Header>
      <abc:Security>
         <abc:UsernameToken>
            <abc:Username>abc</abc:Username>
            <abc:Password>pwd</abc:Password>
         </abc:UsernameToken>
      </abc:Security>
   </soapenv:Header>
   <soapenv:Body>
      <a>
         <b/>
         <c/>
      </a>
   </soapenv:Body>
</soapenv:Envelope>

However, if you can change your stylesheet, I would use something simpler like:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">

    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="headers">
        <xsl:apply-templates select="node()"/>
    </xsl:template>

    <xsl:template match="soapenv:Header">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
            <soapenv:Body>
                <a><b></b><c></c></a>
            </soapenv:Body>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 2

Related Questions