Re: [xsl] Replacing default namespace

Subject: Re: [xsl] Replacing default namespace
From: Abel Braaksma <abel.online@xxxxxxxxx>
Date: Sun, 01 Jul 2007 23:51:44 +0200
Scott Sauyet wrote:
One of the main things I need to do is change the xmlns:tns attribute.

xmlns:tns is not an attribute but a namespace declaration. It looks like an attribute but it is not and cannot be handled as one.


I was naive and thought I could do it just by declaring a new one in my root tag and then mostly doing the identity transform.

In a certain way you are probably right here. But I fail to see what you mean with "changing the xmlns:tns attribute": do you mean, you want the namespace of elements in the namespace pointed to by tns: to point to another namespace? Or do you want the prefix to change? Or something else perhaps? Do you want to globally replace the namespace belonging to tns: or only for certain elements?



The root <definitions> tag in the original WSDL has


xmlns:tns="http://tempuri.org/";

Now throughout the document, elements that look like these:

    <xs:simpleType name="addrFormat" .../>
    <service name="SessionsService" .../>

get transformed into these:

    <xs:simpleType xmlns:tns="http://tempuri.org/"; name="addrFormat">
    <service xmlns:tns="http://tempuri.org/"; name="SessionsService">

There is nothing wrong with this. The elements are still the same. The added namespace does nothing and is probably added by the processor because there are child elements that use the namespace.


There must be some simple way to control this.

To control what?


Any suggestions? The entire (short) WSDL is below.

I would like to help, but you forgot to paste the WSDL. All you showed is the XSLT. I'll try to give some comments on that.


<xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>

- the xml declaration is provided by default, specifying this is redundant.
- the output of method xml is the default, specifying this is redundant (though some consider it good practice to make it explicit)


<xsl:param name="bindings" select="'Service'"/>

you never seem to use this parameter. Did you provide the full XSLT or did you leave parts out?


<xsl:template match="/wsdl:definitions">
<definitions
    targetNamespace="MyNewNamespace"
    xmlns="http://schemas.xmlsoap.org/wsdl/";
    xmlns:xs="http://www.w3.org/2001/XMLSchema";
    xmlns:tns="@Service@"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/";
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/";
    xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/";
    xmlns:ro="urn:Databox"
>

Just out of curiosity: why do you specify all these namespace declarations? You do not use them here and you already specified most of them in the xsl:stylesheet root. This has not additional effect, you can leave them out. Overriding a namespace (like tns: here) will only have effect if you use the prefix explicitly from here, it will not change the namespace of elements that are copied using xsl:copy or xsl:copy-of.


<xsl:template match="wsdl:types">
<types>

Here you remove the namespace information. Is that on purpose? It changes your WSDL to a non-conformant WSDL.


<xsl:template match="xs:schema">
<xs:schema targetNamespace="urn:Databox" xmlns="urn:Databox">

It looks like you can just as well use xsl:copy here.


        <xsl:text>
         </xsl:text>

An empty xsl:text has no effect.



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

This is your general identity template. Do note, however, that elsewhere in your source, you only use bare apply-templates, without a 'select', which means that you only apply elements, not attributes, nor text nodes. Is that on purpose? Do you mean to remove the text nodes there, or there just aren't any?


I have not seen anything in your code where you copy a namespace (but hey, that was why you asked the question, no?). Here's one way to change a namespace axis for a whole document, and leaving the rest intact:

<xsl:template match="*[namespace-uri() = 'http://oldnamespace']">
<xsl:element name="{name()}" namespace="http://newnamespace";>
<xsl:apply-templates select="@* | node()" />
</xsl:element>
</xsl:template>
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy> </xsl:template>



Dealing with the prefix can be done in three ways:


<!-- remove the prefix entirely -->
<xsl:element name="{local-name()}" namespace="http://newnamespace";>

<!-- new prefix -->
<xsl:element name="new-prefix:{local-name()}" namespace="http://newnamespace";>


<!-- leave original prefix, but bind it to a new namespace-->
<xsl:element name="{name()}" namespace="http://newnamespace";>


Hope this helps, Cheers, -- Abel Braaksma

Current Thread