Reputation: 455
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
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
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
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