tags:

views:

402

answers:

2

I'm trying to strip the namespace qualifiers from a document, while retaining the document namespace as the default:

<foo:doc xmlns:foo='somenamespace'>
    <foo:bar />
</foo:doc>

To

<doc xmlns='somenamespace'>
    <bar/>
</doc>

(I know, this is meaningless, but our client doesn't get XML and uses string comparisons to find information in the document.)

I'm using Java's JAXP Transformer API to do my work here. I can strip out all namespace information with this stylesheet, but I want instead to force serialization without prefixes:

<?xml version='1.0' encoding='UTF-8'?> 
    <xsl:stylesheet 
          xmlns:xsl='http://www.w3.org/1999/XSL/Transform'  
          xmlns:xs='http://www.w3.org/2001/XMLSchema' 
          exclude-result-prefixes='xs' 
          version='2.0'> 

      <xsl:output omit-xml-declaration='yes' indent='yes'/>

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

      <xsl:template match='*'> 
        <xsl:element name='{local-name()}'> 
          <xsl:apply-templates select='@*|node()' /> 
        </xsl:element> 
      </xsl:template> 
</xsl:stylesheet>

How can I do this?

+2  A: 

If you want the output to retain the "somenamespace" namespace, but not have a namespace prefix on the elements, declare "somenamenamespace" in the unnamed namespace (without a prefix) in your stylesheet: xmlns='somenamespace'

Then the elements created using the local-name() will have that namespace, but will not have a namespace prefix:

<doc xmlns="somenamespace">
    <bar/>
</doc>

Are you seeing warnings when you execute your stylesheet about ambiguous rule matches?
The template match for "node()" and "*" both trigger a match on an element.

node() is a shortcut for specifying: "*|text()|comment()|processing-instruction()"

You should do one of two things to resolve the ambiguous match:

1.) Change the template match for "@*|node()" to exclude elements by explicitly matching the other node types.

<?xml version='1.0' encoding='UTF-8'?> 
<xsl:stylesheet 
    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'  
    xmlns:xs='http://www.w3.org/2001/XMLSchema' 
    xmlns='somenamespace'
    exclude-result-prefixes='xs' 
    version='2.0'> 

    <xsl:output omit-xml-declaration='yes' indent='yes'/>

    <xsl:template match='@*|text()|comment()|processing-instruction()'> 
        <xsl:copy> 
            <xsl:apply-templates select='@*|node()' /> 
        </xsl:copy> 
    </xsl:template> 

    <xsl:template match='*' > 
        <xsl:element name='{local-name()}'> 
            <xsl:apply-templates select='@*|node()' /> 
        </xsl:element> 
    </xsl:template> 

</xsl:stylesheet>

2.) Add the priority attribute to the template match for "", which bumps up the priority match and ensures that it gets called in favor of the "@|node()".

<?xml version='1.0' encoding='UTF-8'?> 
<xsl:stylesheet 
    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'  
    xmlns:xs='http://www.w3.org/2001/XMLSchema'
    xmlns='somenamespace' 
    exclude-result-prefixes='xs' 
    version='2.0'> 

    <xsl:output omit-xml-declaration='yes' indent='yes'/>

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

    <xsl:template match='*' priority="1"> 
        <xsl:element name='{local-name()}'> 
            <xsl:apply-templates select='@*|node()' /> 
        </xsl:element> 
    </xsl:template> 

</xsl:stylesheet>
Mads Hansen
+1  A: 

Isn't what you want a template matching "sn:*" instead of "*" and having a

<xsl:namespace-alias stylesheet-prefix="sn" result-prefix="#default"/>

in addition? (provided you have xslns:sn="somenamespace")

Michael Krelin - hacker