views:

525

answers:

3
+2  Q: 

Namespaces in XSLT

I'm using XSLT to restructure an XML file. The following code copies all child nodes to a new XML file:

<!--Add all child elements of the zzz node-->
<xsl:template match="zzz">
    <Trade>
     <xsl:attribute name="ID">
      <xsl:value-of select="TradeId" />
     </xsl:attribute>
     <xsl:copy-of select="*"></xsl:copy-of>
    </Trade>
</xsl:template>

I would like to modify the code so that it puts the nodes into a specific namespace that is not in the source document. What do I need to change?

A: 

You can just include the namespace in your stylesheet like so:

<!--Add all child elements of the zzz node-->
<xsl:template match="zzz" xmlns:my="your target ns">
<my:Trade>
<xsl:attribute name="my:ID" >
<xsl:value-of select="TradeId" />
</xsl:attribute>
<xsl:copy-of select ="*"></xsl:copy-of>
</my:Trade>
</xsl:template>

Edit: As Dimitre pointed out, this will not place the copied nodes into the new namespace, only the Trade element.

Teun D
Quite incorrect: 1. The OP made clear he wants to put in a new namespace only the child nodes of the matched element -- not any attribute. 2. The elements copied inside <my:Trade> still do not belong to this new namespace.
Dimitre Novatchev
Ah, right. I misunderstood the question. Thanks, Dimitre.
Teun D
+3  A: 

The previous two answers (by teun and Craig Bovis) are not correct -- see my comments to each of these.

The corect way to move a given element to a new namespace involves re-creating this element as shown below:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="zzz">
      <trade ID="{TradeId}">
        <xsl:apply-templates select="*[not(self::TradeId)]"/>
      </trade>
    </xsl:template>

    <xsl:template match="zzz/*">
      <xsl:element name="{name()}" namespace="my:Trade">
        <xsl:copy-of select="@* | node()"/>
      </xsl:element>
    </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following source XML document:

<zzz>
    <TradeId>153</TradeId>
    <x:item xmlns:x="x:x">A</x:item>
    <x:item xmlns:x="x:x">B</x:item>
    <x:item xmlns:x="x:x">C</x:item>
</zzz>

the required result is produced:

<trade ID="153">
   <x:item xmlns:x="my:Trade">A</x:item>
   <x:item xmlns:x="my:Trade">B</x:item>
   <x:item xmlns:x="my:Trade">C</x:item>
</trade>
Dimitre Novatchev
Thank you very much.
macleojw
A: 

Dimitre Novatchev's solution is fine, but I would also note that if you need to change namespaces of nested elements too, the following will work better:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:output omit-xml-declaration="yes" indent="yes"/>

  <xsl:template match="zzz">
    <trade ID="{TradeId}">
      <xsl:apply-templates select="*[not(self::TradeId)]" mode="change-ns"/>
    </trade>
  </xsl:template>

  <xsl:template match="@*|node()" priority="-10" mode="change-ns">
    <xsl:copy/>
  </xsl:template>

  <xsl:template match="*" mode="change-ns">
    <xsl:element name="{name()}" namespace="my:Trade">
      <xsl:apply-templates select="@*|node()" mode="change-ns"/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

E.g. if you have the following input document

<trade ID="153">
  <x:item xmlns:x="my:Trade" someattr="1">
    <x:subitem anotherattr="2">A1</x:subitem>
    <x:subitem anotherattr="3">A2</x:subitem>
  </x:item>
  <x:item xmlns:x="my:Trade">B</x:item>
  <x:item xmlns:x="my:Trade">C</x:item>
</trade>

you will get

<zzz>
  <TradeId>153</TradeId>
  <x:item xmlns:x="x:x" someattr="1">
    <x:subitem anotherattr="2">A1</x:subitem>
    <x:subitem anotherattr="3">A2</x:subitem>
  </x:item>
  <x:item xmlns:x="x:x">B</x:item>
  <x:item xmlns:x="x:x">C</x:item>
</zzz>

Attributes are added to demonstrate that they're copied correctly, and separate mode is used for namespace changing templates so that they don't interfere with other code in case you want to use them as a part of larger stylesheet.

fionbio