Date Tags xml / xslt

Every now and then I have to re-learn xslt to transform xml documents. My mantra is to use the right tool for the right job… so I’m here again struggling with xslt. There is a bit of a love/hate relation between me and this language. I’ve used a lot during my master thesis transforming an enormous - industry size - software specification (in SDL) to a formal language. This was a very painful way of learning a new language, After that I just used it to perform small tasks, but I always manage to forget part of the specification…

Anyway, just to avoid repeating this error again, the lesson today is about xml namespaces. | Here you can find a lengthy explanation about namespace and all possible related problems to xslt. I stumbled on a very simple case. Why my xslt stylesheet does not work in the presence of namespace ?

A small motivating example. Suppose you have a very simple xml document

<doc xmlns="http://mynamespace.org">
  <a><b>aaa</b><c>bbb</c></a>
</doc>

and you want to transform it in a different xml document as :

<?xml version="1.0"?>
<newdoc xmlns="http://othernamespace.org">
  <a xmlns="http://mynamespace.org"><c>bbb</c></a>
<b xmlns="http://mynamespace.org"><![CDATA[aaa]]></b></newdoc>

Therefore you want to - change the document root (and namespace) - copy all the content of the element but the element <code lang="xml"><b> - move the element below <code lang="xml"><a> - embed the text content of ``` in a CDATA section.

We go one step at the time. First we transform the root element and we change the default namespace. The header of the xsl file is standard except for the declaration of the attribute xmlns:ns="http://mynamespace.org". Since our source xml document as a namespace, we have to match elements in this namespace. In other to do so, we associate a labelnsto the namespacehttp://mynamespace.organd the we use it to match the element <code lang="xml"><doc>.

<?xml version="1.0"?>
<xsl:transform
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:ns="http://mynamespace.org">

The second part is the standard way of copying nodes and contents…

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

In the third part we match the root element, we create a new element and we copy everything. Since ``` does not have a select it applies by default to all nodes inside the root element.

<xsl:template match="ns:doc">
  <xsl:element name="newdoc" namespace="http://mynamespace.org">
    <xsl:apply-templates/>
  </xsl:element>
</xsl:template>

</xsl:transform>

The result :

<?xml version="1.0"?>
<newdoc xmlns="http://othernamespace.org">
  <a xmlns="http://mynamespace.org"><b>aaa</b><c>bbb</c></a>
</newdoc>

Now we want to move the element . We add a new template to match <code lang="xml"><a> and copy all its content except the node ```

<xsl:template match="ns:a">
  <xsl:copy>
    <xsl:apply-templates select="*[not(self::ns:b)]"/>
  </xsl:copy>
</xsl:template>

This template being more specific then the default template we specified at the beginning of the document will be applied to . Then we have to modify the template for the root element in order to copy the content of <code lang="xml"><a> and the the content of ``` below.

<xsl:template match="ns:doc">
  <xsl:element name="newdoc" namespace="http://mynamespace.org">
    <xsl:apply-templates/>
    <xsl:apply-templates select="ns:a/ns:b"/>
  </xsl:element>
</xsl:template>

This will give something like this :

<?xml version="1.0"?>
<newdoc xmlns="http://othernamespace.org">
  <a xmlns="http://mynamespace.org"><c>bbb</c></a>
<b xmlns="http://mynamespace.org">aaa</b></newdoc>

Last we want to embed the content of ``` in a cdata section. This is easy as we just need to add an output directive at the beginning of the file. The complete example :

<?xml version="1.0"?>
<xsl:transform
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:ns="http://mynamespace.org">

<xsl:output method="xml" indent="yes" cdata-section-elements="ns:b" />

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

<xsl:template match="ns:a">
  <xsl:copy>
    <xsl:apply-templates select="*[not(self::ns:b)]"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="ns:doc">
  <xsl:element name="newdoc" namespace="http://othernamespace.org">
    <xsl:apply-templates/>
    <xsl:apply-templates select="ns:a/ns:b"/>
  </xsl:element>
</xsl:template>

</xsl:transform>
```xml

And the final result :

<?xml version="1.0"?>
<newdoc xmlns="http://othernamespace.org">
  <a xmlns="http://mynamespace.org"><c>bbb</c></a>
<b xmlns="http://mynamespace.org"><![CDATA[aaa]]></b></newdoc>