问题
I have this soap request :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<soapenv:Header>
<wsse:Security mustUnderstand="1">
<wsse:UsernameToken>
<wsse:Username>test</wsse:Username>
<wsse:Password>test</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<hel:docTypeRef_tns_sayHello xmlns:hel="http://aaa/bbb.fr">
<arg0>John</arg0>
<arg1>111-222-333</arg1>
</hel:docTypeRef_tns_sayHello>
</soapenv:Body>
</soapenv:Envelope>
I would like first to change the namespace uri appearing in the hel:docTypeRef_tns_sayHello element to something else (ex : http://test.fr). This namespace definition can appear in the hel:docTypeRef_tns_sayHello element as in the code above or in the root Envelope element, so i'd like to add this namespace definition only in the Envelope element
Then I'd like to modify the value of mustUnderstand attribute to 0.
The result should be of the form :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext- 1.0.xsd"
**xmlns:hel="http://test.fr"**>
<soapenv:Header>
<wsse:Security mustUnderstand="**0**">
<wsse:UsernameToken>
<wsse:Username>test</wsse:Username>
<wsse:Password>test</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<hel:docTypeRef_tns_sayHello>
<arg0>John</arg0>
<arg1>111-222-333</arg1>
</hel:docTypeRef_tns_sayHello>
</soapenv:Body>
</soapenv:Envelope>
Could somebody help me ? Thancks !
回答1:
I. Here is a generic, parameterized XSLT 1.0 solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pElemName" select="'hel:docTypeRef_tns_sayHello'"/>
<xsl:param name="pOldNamespace" select="'http://aaa/bbb.fr'"/>
<xsl:param name="pNewNamespace" select="'http://test.fr'"/>
<xsl:variable name="vPrefix" select="substring-before($pElemName, ':')"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:choose>
<xsl:when test=
"not(name() = $pElemName
and
namespace-uri() = $pOldNamespace
)
">
<xsl:call-template name="identity"/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{name()}" namespace="{$pNewNamespace}">
<xsl:copy-of select=
"namespace::*
[not(name() = $vPrefix
and
. = $pOldNamespace
)
]"/>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()" mode="removeNS"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="*" mode="removeNS">
<xsl:choose>
<xsl:when test=
"not(starts-with(name(), $vPrefix)
and
namespace-uri() = $pOldNamespace
)">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select=
"namespace::*
[not(name() = $vPrefix
and
. = $pOldNamespace
)
]"/>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()" mode="removeNS"/>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="@mustUnderstand">
<xsl:attribute name="{name()}">0</xsl:attribute>
</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:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<soapenv:Header>
<wsse:Security mustUnderstand="1">
<wsse:UsernameToken>
<wsse:Username>test</wsse:Username>
<wsse:Password>test</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<hel:docTypeRef_tns_sayHello xmlns:hel="http://aaa/bbb.fr">
<arg0>John</arg0>
<arg1>111-222-333</arg1>
</hel:docTypeRef_tns_sayHello>
</soapenv:Body>
</soapenv:Envelope>
the wanted, correct result is produced:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<soapenv:Header>
<wsse:Security mustUnderstand="0">
<wsse:UsernameToken>
<wsse:Username>test</wsse:Username>
<wsse:Password>test</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<hel:docTypeRef_tns_sayHello xmlns:hel="http://test.fr">
<arg0>John</arg0>
<arg1>111-222-333</arg1>
</hel:docTypeRef_tns_sayHello>
</soapenv:Body>
</soapenv:Envelope>
Do note: In XSLT 1.0 it isn't possible without using an extension function (at least xxx:node-set()
)to dynamically add a new namespace (that doesn't exist already as a namespace node) to an element. In XSLT 2.0 the new xsl:namespace
instruction can be used to dynamically construct a new namespace node from dynamic (statically unknown) parts.
Here is a small example how with XSLT 1.0 to add a new, non-existent at the start of the transformation namespace node:
<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:template match="/*">
<xsl:variable name="vrtfDoc">
<t xmlns:hel="http://test.fr"/>
</xsl:variable>
<xsl:variable name="vNS" select=
"ext:node-set($vrtfDoc)/*/namespace::*
[name()='hel']"/>
<xsl:element name="{name()}"
namespace="{namespace-uri()}">
<xsl:copy-of select="namespace::*"/>
<xsl:copy-of select="$vNS"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on this XML document:
<t xmlns:someNS="some:NS"/>
it re-creates the top element t
with all its namespace nodes and adds one new namespace node to it:
<t xmlns:someNS="some:NS" xmlns:hel="http://test.fr"/>
II. Here is a full, XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pElemName" select="'hel:docTypeRef_tns_sayHello'"/>
<xsl:param name="pOldNamespace" select="'http://aaa/bbb.fr'"/>
<xsl:param name="pNewNamespace" select="'http://test.fr'"/>
<xsl:variable name="vPrefix" select="substring-before($pElemName, ':')"/>
<xsl:template match="node()|@*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:namespace name="{$vPrefix}" select="$pNewNamespace"/>
<xsl:copy-of select=
"namespace::*
[not(name() = $vPrefix
and
. = $pOldNamespace
)
]"/>
<xsl:apply-templates select="node()|@*"/>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:choose>
<xsl:when test=
"not(name() = $pElemName
and
namespace-uri() = $pOldNamespace
)
">
<xsl:call-template name="identity"/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{name()}" namespace="{$pNewNamespace}">
<xsl:copy-of select=
"namespace::*
[not(name() = $vPrefix
and
. = $pOldNamespace
)
]"/>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()" mode="removeNS"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="*" mode="removeNS">
<xsl:choose>
<xsl:when test=
"not(starts-with(name(), $vPrefix)
and
namespace-uri() = $pOldNamespace
)">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select=
"namespace::*
[not(name() = $vPrefix
and
. = $pOldNamespace
)
]"/>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="node()" mode="removeNS"/>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="@mustUnderstand">
<xsl:attribute name="{name()}">0</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document (above), the wanted, correct result is produced:
<soapenv:Envelope xmlns:hel="http://test.fr"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<soapenv:Header>
<wsse:Security mustUnderstand="0">
<wsse:UsernameToken>
<wsse:Username>test</wsse:Username>
<wsse:Password>test</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<hel:docTypeRef_tns_sayHello>
<arg0>John</arg0>
<arg1>111-222-333</arg1>
</hel:docTypeRef_tns_sayHello>
</soapenv:Body>
</soapenv:Envelope>
来源:https://stackoverflow.com/questions/8870195/how-to-change-namespace-uri-using-xsl-in-soap-request