Add or remove a namespace to/from an xml
Facts - XSLT
Thursday, 01 September 2011 20:15

Adding namespaces

The following XSLT 1.0 stylesheet replaces a namespace on the elements of an xml:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:param name="uri">default</xsl:param>
 
  <xsl:template match="*">
        <xsl:element name="{local-name()}" namespace="{ $uri }" >
            <xsl:copy-of select="attribute::*"/>
            <xsl:apply-templates />
        </xsl:element>
  </xsl:template>
 
</xsl:stylesheet>

Example xml:

<foo x="1">
    <bar y="2">def
        <baz z="3">abc</baz>
    </bar>
</foo>

Use the following xsltproc command line to add a namespace:

$ xsltproc.exe --output test2.xml --param uri "'http://newnamespace.com'" replaceNamespace.xslt test.xml

The output file test2.xml:

<?xml version="1.0"?>
<foo xmlns="http://newnamespace.com" x="1">
    <bar y="2">def
        <baz z="3">abc</baz>
    </bar>
</foo>

Dealing with references using elementFormDefault and attributeFormDefault

I had to add namespaces to create an xml such that it conformed to an xsd with the following lines:

<xs:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0"
targetNamespace="http://factsandpeople.com/schemas/ns/xsd" xmlns:tns="http://factsandpeople.com/schemas/ns/xsd">
  <xs:element name="element1">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:decimal">
          <xs:attribute ref="tns:code1" use="required"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
  <xs:attribute name="code1" type="xs:string"/>
 
  <xs:element name="element2">
    <xs:complexType>
      <xs:simpleContent>
        <xs:extension base="xs:decimal">
          <xs:attribute name="code2" use="required" type="xs:string"/>
        </xs:extension>
      </xs:simpleContent>
    </xs:complexType>
  </xs:element>
....
</xs:schema>
 

XML instances conforming to this xsd should have attribute "code1" with an explicit namespace and attribute "code2" without the namespace:

 
<?xml version="1.0"?>
...
  <tns:element1 tns:code1="xyz">3</tns:element1>
  <tns:element2 code2="abc">4</tns:element2>

To get the add namespace xslt working I had to switch to qualified attributes in the xsd instead of unqualified attributes. If you have an xsd with unqualified elements then you can have the same problem with references to elements. The solution is then making the elements qualified in the xsd. To get the desired output I also had to change my xslt:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:param name="uri">default</xsl:param>
 
  <xsl:template match="*">
        <xsl:element name="{local-name()}" namespace="{ $uri }" >
            <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
  </xsl:template>
 
  <xsl:template match="@*">
    <xsl:attribute name="{local-name()}" namespace="{ $uri }">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:template>
 
</xsl:stylesheet>

Removing namespaces

To remove the namespace the same stylesheet can be used, resulting in the original xml. Consider the input xml below:

<?xml version="1.0"?>
<foo xmlns="http://testnamespace.com" x="1">
    <bar y="2">def
        <baz z="3">abc</baz>
    </bar>
    <a-special-element xmlns="http://test.com/" xmlns:test="http://test.com/" test:n="8"/>
</foo>

The command line to remove the namespaces:

$ xsltproc.exe --param uri "''" replaceNamespace.xslt test3.xml

The actual output:

<?xml version="1.0"?>
<foo x="1">
    <bar y="2">def
        <baz z="3">abc</baz>
    </bar>
    <a-special-element xmlns:test="http://test.com/" test:n="8"/>
</foo>

Note that the namespace on the attribute has not been removed. In addition the output should have an empty xmlns declaration, which is not present in the original xml. This is a feature of the xsltproc tool. The output should have been:

<foo x="1" xmlns="">
    <bar y="2">def
        <baz z="3">abc</baz>
    </bar>
    <a-special-element xmlns:test="http://test.com/" test:n="8"/>
</foo>

The development tool Eclipse (version 3 and 4), for example, treats an empty xmlns attribute different from a non existing xmlns attribute. Therefore it is better to remove the namespace with the following stylesheet instead, from Dave Pawson's website. Note that this stylesheet removes namespaces on the attributes as well:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
 
  <xsl:template match="/|comment()|processing-instruction()">
    <xsl:copy>
      <!-- go process children (applies to root node only) -->
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>
 
  <xsl:template match="*">
    <xsl:element name="{local-name()}">
      <!-- go process attributes and children -->
      <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
  </xsl:template>
 
  <xsl:template match="@*">
    <xsl:attribute name="{local-name()}">
      <xsl:value-of select="."/>
    </xsl:attribute>
  </xsl:template>
 
</xsl:stylesheet>