Search code examples
xmlxsltxml-namespacesxalan

XSL to transform only elements in a certain namespace


I have an xml document that is structured somewhat like this :-

<catalog xmlns="format_old" xmlns:final="format_new">
  <final:book>
    <final:title>blah</final:title>
    <final:author>more blah</final:author>
  </final:book>
  <book>
    <description title="blah2"/>
    <writer name="more blah2"/>
  </book>
</catalog>

Obviously, this is a simplified version of the problem. What I want to do is to convert this into something like :-

<catalog xmlns="format_new">
  <book>
    <title>blah</title>
    <author>more blah</author>
  </book>
  <book>
    <title>blah2</title>
    <author>more blah2</author>
  </book>
</catalog>

The stylesheet that I have right now is something like this :-

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:orig="format_old"
  xmlns="format_new"/>

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

<xsl:template match="//orig:book">
  <xsl:element name="title">
    <xsl:value-of select="./orig:description/@title" />
  </xsl:element>
  <xsl:element name="author">
    <xsl:value-of select="./orig:writer/@name" />
  </xsl:element>
</xsl:template>

</xsl:stylesheet>

This gives me an output like :-

<catalog xmlns="format_old">
  <book xmlns="format_new">
    <title>blah</title>
    <author>more blah</author>
  </book>
  <book xmlns:orig="format_old" xmlns="format_new">
    <title>blah2</title>
    </author>more blah2</author>
  </book>
</catalog>

There are two problems with this stylesheet :-

1.) (major issue) The root element gets copied over rather than changing the default namespace of the root element. So basically the catalog element would still be in the namespace format_old.

2.) (minor issue) This will convert the elements as :-

<book xmlns:orig="format_old" xmlns="format_new">
  ...
</book>

instead of picking up the namespace from the root element as keeping it as

<book>
  ...
</book>

What am I missing here? I'm using Xalan-C.


Solution

  • I think the following should do:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
        xmlns="format_new"
        xmlns:ns1="format_old"
        exclude-result-prefixes="ns1"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    <xsl:template match="@* | text() | comment() | processing-instruction()">
      <xsl:copy/>
    </xsl:template>
    
    <xsl:template match="*">
      <xsl:element name="{local-name()}">
        <xsl:apply-templates select="@* | node()"/>
      </xsl:element>
    </xsl:template>
    
    <xsl:template match="ns1:book/ns1:description[@title]">
      <title>
        <xsl:value-of select="@title"/>
      </title>
    </xsl:template>
    
    <xsl:template match="ns1:book/ns1:writer[@name]">
      <author>
        <xsl:value-of select="@name"/>
      </author>
    </xsl:template>
    
    </xsl:stylesheet>
    

    Saxon 6.5.5 transforms your input into

    <?xml version="1.0" encoding="utf-8"?><catalog xmlns="format_new">
      <book>
        <title>blah</title>
        <author>more blah</author>
      </book>
      <book>
        <title>blah2</title>
        <author>more blah2</author>
      </book>
    </catalog>