XSLT Copy with Exception

A recent problem that was posed to me concerned how to copy the entire contents of an XML document with certain exceptions.  Turns out that the simplest way to handle this requirement in XSLT1.0 was to include the standard XSL identity template in my stylesheet and add another template to handle the exception.

A simple example will make things clearer.  Suppose we have the following XML document (which I shamelessly copied from W3Schools.com and modified to simplify) and we want to copy this document in its entirety except for details of CDs by a specific artist.
<?xml version="1.0"?>
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<YEAR>1988</YEAR>
</CD>
<CD>
<TITLE>Greatest Hits</TITLE>
<ARTIST>Dolly Parton</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>RCA</COMPANY>
<YEAR>1982</YEAR>
</CD>
<CD>
<TITLE>One night only</TITLE>
<ARTIST>Bee Gees</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Polydor</COMPANY>
<YEAR>1998</YEAR>
</CD>
<CD>
<TITLE>Sylvias Mother</TITLE>
<ARTIST>Dr.Hook</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS</COMPANY>
<YEAR>1973</YEAR>
</CD>
<CD>
<TITLE>Maggie May</TITLE>
<ARTIST>Rod Stewart</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Pickwick</COMPANY>
<YEAR>1990</YEAR>
</CD>
</CATALOG>
Below is the stylesheet.  It accepts one parameter, i.e. the name, or part of the name, of an artist.  It copies all nodes and attributes to the output document except those nodes and attributes related to the artist whose name matches or is is a superset of the inputted string.
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:param name="artist"></xsl:param>

<xsl:output method="xml"/>

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

<xsl:template match="/CATALOG/CD">
<xsl:if test="not(contains(./ARTIST,$artist))">
<xsl:copy-of select="." />
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The first template is the standard identity template from the XSL 1.0 Transformations Recommendation.  It matches all attributes and all nodes being children of other nodes, and copies them to the output document.   The second template cancels the automatic copying of the <CD> nodes and children, checks to see that the name, or part of the name, of the artist does not match the search string, and only does a deep copy of the nodes and attributes to the output document if there is no match.

Assuming you are on a Linux system, you can use the command line utility xsltproc (which is part of the XSLT C library for GNOME) to transform the input document into the following output document when "Stewart" is passed as a parameter to the stylesheet.  BTW, note the use of Java style string quotes for the parameter string.
$ xsltproc -param artist "'Stewart'" file.xsl file.xml
Here is the output document. It is a copy of the input document except the "record" for the Rod Stewart's CD is not included.
<?xml version="1.0"?>
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<YEAR>1988</YEAR>
</CD>
<CD>
<TITLE>Greatest Hits</TITLE>
<ARTIST>Dolly Parton</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>RCA</COMPANY>
<YEAR>1982</YEAR>
</CD>
<CD>
<TITLE>One night only</TITLE>
<ARTIST>Bee Gees</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Polydor</COMPANY>
<YEAR>1998</YEAR>
</CD>
<CD>
<TITLE>Sylvias Mother</TITLE>
<ARTIST>Dr.Hook</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS</COMPANY>
<YEAR>1973</YEAR>
</CD>
</CATALOG>
I hope this example will help you simplify your stylesheet the next time you have to copy a document with exceptions.  Functional programming is a very powerful paradigm but sometimes it is difficult to quickly formulate a simple and elegant solution if you have spent years programming using object-oriented or imperative paradigms.

0 comments:

Post a Comment