Search code examples
xsltxslt-1.0xml-namespaces

xsl:copy-of semantics and superfluous namespaces


I've asked a very similar question which was a duplicate, and this is almost a duplicate of that one EXCEPT the solution to the original question has an issue with it.

This is an irritating question because it revolves around the representation of the the xml, not its meaning - i.e. the problem is that customers will sometimes reject a technically valid program output because it "looks" wrong...even when it isnt.

msxsl (from dotnet core 7) xslt 1.0

consider this:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:kookerella="kookerella.com"
    xmlns:foo="foo.com"
    exclude-result-prefixes="msxsl foo">
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="/">
      <xsl:variable name="foo">
         <foo:data>
            <foo:ignore />
            <kookerella:wibble value="1"/>
         </foo:data>
      </xsl:variable>
      <kookerella:root>
         <xsl:copy-of select="msxsl:node-set($foo)/foo:data/kookerella:wibble"/>
      </kookerella:root>
   </xsl:template>
</xsl:stylesheet>

the crux is the namespace "foo" is excluded from the output.

run it (against anything) and we get

<kookerella:root xmlns:kookerella="kookerella.com">
  <kookerella:wibble value="1" xmlns:foo="foo.com" />
</kookerella:root>

the nasty "foo" is there (perfectly valid - but I did exclude it)...this is the subject of my original question which was a duplicate of another question where the solution is to do this. original question/solution

so this is the solution using that answer:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:kookerella="kookerella.com"
    xmlns:foo="foo.com"
    exclude-result-prefixes="msxsl foo"
>
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="/">
      <xsl:variable name="foo">
         <foo:data>
            <foo:ignore />
            <kookerella:wibble value="1"/>
         </foo:data>
      </xsl:variable>
      <kookerella:root>
         <xsl:apply-templates select="msxsl:node-set($foo)/foo:data/kookerella:wibble" mode="copy-no-namespaces"/>
      </kookerella:root>
   </xsl:template>

   <xsl:template match="*" mode="copy-no-namespaces">
      <xsl:element name="{local-name()}" namespace="{namespace-uri()}">
         <xsl:copy-of select="@*"/>
         <xsl:apply-templates select="node()" mode="copy-no-namespaces"/>
      </xsl:element>
   </xsl:template>

   <xsl:template match="comment()| processing-instruction()" mode="copy-no-namespaces">
      <xsl:copy/>
   </xsl:template>   
</xsl:stylesheet>

and we get this:

<kookerella:root xmlns:kookerella="kookerella.com">
  <wibble value="1" xmlns="kookerella.com" />
</kookerella:root>

correct...its worked...foo is gone, but (I don't know the correct jargon for this) the namespace is now encoded explicitly in the "xmlns" attribute.

how do we get it to use the "kookerella" namespace defined in the root? (irritatingly) how do we make it 'look' like the original representation but without 'foo'.


Solution

  • If you use <xsl:element name="{name()}" namespace="{namespace-uri()}"> instead of <xsl:element name="{local-name()}" namespace="{namespace-uri()}"> you might get what you want for that input sample.