Reputation: 29
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
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:
The identity rule/template copies every node "as-is".
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
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