3

I am using Microsoft's XSLT processor (1.0 only)

XML opening lines:

<?xml version="1.0" encoding="utf-8"?>
<Header xmlns="http:\\OldNameSpace.com">
    <Detail>

Have the following XSLT template to pick up the <Header> element of my document and change its namespace.

<xsl:template match="*">
    <xsl:element name="{name()}" xmlns="http:\\NewNameSpace.com"> 
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates />
    </xsl:element>
</xsl:template>

Which turns <Header xmlns="http:\\OldNameSpace.com"> Into <Header xmlns="http:\\NewNameSpace.com">

However I now need to add a second namespace to this so that I get the following output:

<Header xmlns="NewNameSpace.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

I have tried using:

<xsl:template match="*">
    <xsl:element name="{name()}" xmlns="NewNameSpace.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates />
    </xsl:element>
</xsl:template>

However I still only get the same output as the original XSLT template.

Can anyone enlighten to me as to why this is?

Mike
  • 618
  • 2
  • 8
  • 27

3 Answers3

5

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:old="http:\\OldNameSpace.com"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  exclude-result-prefixes="old xsi">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pNewNamespace" select="'http:\\NewNameSpace.com'"/>

 <xsl:variable name="vXsi" select="document('')/*/namespace::*[name()='xsi']"/>

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

    <xsl:template match="old:*">
        <xsl:element name="{local-name()}" namespace="{$pNewNamespace}">
          <xsl:copy-of select="$vXsi"/>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

when applied on the following XML document:

<Header xmlns="http:\\OldNameSpace.com">
    <Detail/>
</Header>

produces (what I guess is) the wanted, correct result:

<Header xmlns="http:\\NewNameSpace.com"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <Detail/>
</Header>
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • @Mike: You are welcome. Your problem is easy to solve, because the namespace to add is statically known. It becomes more interesting (in XSLT 1.0) when the namespace is to be dynamically generated. Both David Carlisle and Michael Kay hinted about the solution in this, more challenging case. Recently I gave an answer using this special XSLT 1.0 technique -- it also requires the use of the `xxx:node-set()` extension function. – Dimitre Novatchev Apr 11 '12 at 12:33
  • @Mike: The solution to the more challenging problem: http://stackoverflow.com/a/9937128/36305 – Dimitre Novatchev Apr 11 '12 at 12:40
  • Luckily it is a static in this context then. Shall have a read of the more complex solution out of interest and learning. – Mike Apr 11 '12 at 12:43
  • 1
    Ran this using microsofts XSLT processor and it ran fine, when run in the whole application it throws an exception during the convert of: "This operation is not supported for a relative URI" Any ideas as to what is causing this? – Mike Apr 11 '12 at 12:56
  • @Mike: The cause is obviously in the code that you haven't shown to us. I'd recommend to ask a new question with a complete (but as small as possible) example that people can use to repro the problem. – Dimitre Novatchev Apr 11 '12 at 13:00
  • Moved to new question: http://stackoverflow.com/questions/10106640/xslt-unsupported-operation-c-sharp – Mike Apr 11 '12 at 13:15
2

xsl:element (unlike literal result elements) does not copy all in scope namespaces to the result, just the namespace required for the element name 9either implicitly from its name or as specified with the namespace argument).

xslt2 adds an xsl:namespace instruction for this case but in xslt1 the easiest thing to do is

where

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

somewhere on an ancestor (eg on xsl:stylesheet.)

that will add a spurious xsi:tmp="" to the output but also then a namespace declaration, If you actually need an attribute in this namespace eg xsi:type use that instead of tmp in the above and you are done. If you don't mind the extra, possibly invalid attribute in the xsi namespace you are done. Otherwise do the above in a variable, then use msxsl:node-set to query in to the variable and remove the spurious extra attribute.

David Carlisle
  • 5,582
  • 1
  • 19
  • 23
  • Do you mind clarifying a bit further how this implements as not the most experienced with xslt. Implementing `xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"` into the xsl:stylesheet declaration seemed to make no difference to my output at all – Mike Apr 11 '12 at 09:08
  • try http://fgeorges.blogspot.co.uk/2007/01/creating-namespace-nodes-in-xslt-10.html – David Carlisle Apr 11 '12 at 09:10
  • Will look more into that blog soon, it looked a good read from what I read of it, now currently querying the use of the 2nd namespace at all in the document as I don't see any use for the three things it allows us to do in which case its pointless. Will delete/continue this thread when I know if its needed – Mike Apr 11 '12 at 09:42
2

If you know statically what namespace you want to generate, then the easiest way to do it in XSLT 1.0 is using xsl:copy-of. Create a source document <dummy xmlns:xsi="http://whatever"/>, and then do <xsl:copy-of select="document('dummy.xml')/*/namespace::xsi"/> inside your call of xsl:element.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164