[xsl] Removing namespaces from source document (long)

Subject: [xsl] Removing namespaces from source document (long)
From: "Stephen Ng" <stephen.ng@xxxxxxxxxxx>
Date: Sun, 7 Apr 2002 18:46:10 -0400
I have the feeling this is a newbie faq thing, but I'm still confused after
doing a bit of searching this weekend so I'm posting....

I have some (unused) namespace declarations in my source xml document that I
want to banish.

For example:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:good-ns="http://good.com"; xmlns:bad-ns="http://bad.com";>
  <parent color="red">
   <good-ns:child shape="square">
    some text
    </good-ns:child>
  </parent>
</root>

I want to transform this to get:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:good-ns="http://good.com";>
  <parent color="red">
   <good-ns:child shape="square">
    some text
    </good-ns:child>
  </parent>
</root>

I understand that, in general, the xslt processor must emit the "bad-ns"
declaration at the top because it can't tell that it isn't used later.

So, of course, an identity template just gives me the same thing

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>
  <xsl:template match="node()|@*">
    <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>

Jeni Tennison in her book seems to be saying this is because I'm doing a
copy-of.  Quoting liberally:
---
When you copy an element from the source tree using xsl:copy-of, the
element, all its namespace nodes, all its attributes, and all its children
are copied - you generate a deep copy. This means that any namespaces in
scope for that element in the source have to be in scope for that element in
the result. Because source XML often declares namespaces that you're not
interested in, this can lead to unwanted namespace declarations.

You can remove these namespace declarations by using a recursive shallow
copy, rather than the deep copy generated by xsl:copy-of. You can generate
shallow copies recursively with an identity template, as shown in the
following example:

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

But if I do:

<xsl:template match="/">
  <xsl:apply-templates mode="copy"/>
</xsl:template>

plus her template, I get the same results:  bad-ns is still declared.  Am I
misunderstanding her example?

Ken Holman in
http://www.biglist.com/lists/xsl-list/archives/200003/msg00303.html has an
example of a stylesheet which omits a namespace which originates from the
source document, but it's done by hard-wiring the element name inside the
stylesheet:

<xsl:template match="/">                         <!--root rule-->
  <result>
   <xsl:apply-templates select="/doc/para"/>
  </result>
</xsl:template>

<xsl:template match="para">
  <para>
   <xsl:apply-templates/>
  </para>
</xsl:template>

This is not general though, so I wrote:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:good-ns="http://good.com";>
  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>
  <xsl:template match="*">
    <xsl:element name="{name()}">
      <xsl:apply-templates select="node()|@*"/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="@*">
    <xsl:copy/>
  </xsl:template>
</xsl:stylesheet>

which seems to work.

The "good" namespace must be declared for <xsl:element> to properly emit the
element.

So, my questions are:

Is this correct?  and Is there a better way to do this? and if not, Isn't
this a bit convoluted for what seems like a pretty common task?

Thanks,

Stephen Ng
Lumigent


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread