views:

2861

answers:

3

I've got some XML which declares a namespace which is only used for attributes, like this:

<?xml version="1.0" encoding="UTF-8"?>
<a xmlns:x="http://tempuri.com"&gt;
    <b>
     <c x:att="true"/>
     <d>hello</d>
    </b>
</a>

I want to use XSL to create a copy of selected nodes and their values - getting rid of the attributes. So my desired output is:

<?xml version="1.0" encoding="UTF-8"?>
<b>
    <c />
    <d>hello</d>
</b>

I've got some XSL that almost does this, but I can't seem to stop it putting the namespace declaration in the top level element of the output. My XSL is:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
     <xsl:apply-templates select="/a/b"/>
    </xsl:template>

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

The first element of the output is <b xmlns:x="http://tempuri.com"&gt; instead of <b>. I've tried declaring the namespace in the XSL and putting the prefix in the exclude-result-prefixes list, but this doesn't seem to have any effect. What am I doing wrong?

UPDATE: I've found that by declaring the namespace in the XSL and using the extension-element-prefixes attribute works, but this doesn't seem right! I guess I could use this, but I'd like to know why the exclude-result-prefixes doesn't work!

UPDATE: Actually, it seems this extension-element-prefixes solution only works with XMLSpy's built-in XSLT engine, not with MSXML.

+2  A: 
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
    xmlns:x="http://tempuri.com"&gt;
    <xsl:template match="/">
     <xsl:apply-templates select="/a/b"/>
    </xsl:template>

    <xsl:template match="*">
     <xsl:element name="{local-name(.)}">
      <xsl:apply-templates/>
     </xsl:element>
    </xsl:template>

    <xsl:template match="@*">
     <xsl:copy/>
    </xsl:template>

    <!-- This empty template is not needed.
Neither is the xmlns declaration above:
    <xsl:template match="@x:*"/> -->
</xsl:stylesheet>

I found an explanation here.

Michael Kay wrote:
exclude-result-prefixes only affects the namespaces copied from the stylesheet by a literal result element, it doesn't affect copying of namespaces from source documents.

steamer25
Thanks, this does work (although the "@*" template doesn't seem to be required), but I thought the "local-name()" function was pretty slow..?
Graham Clark
I want XSLT 2.0!
Riri
+2  A: 
<xsl:stylesheet 
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:x="http://tempuri.com"
  exclude-result-prefixes="x"
>

  <!-- the identity template copies everything 1:1 -->
  <xsl:template match="@* | node()">
     <xsl:copy>
      <xsl:apply-templates select="@* | node()" />
    </xsl:copy>
  </xsl:template>

  <!-- this template explicitly cares for namespace'd attributes -->
  <xsl:template match="@x:*">
    <xsl:attribute name="{local-name()}">
      <xsl:value-of select="." />
    </xsl:attribute>
  </xsl:template>

</xsl:stylesheet>
Tomalak
A: 

Tomalak, That was just brilliant. Thanks, Vikas

VIkas