Friday, August 12, 2005

An Actual, Real, Everyday Use for XSLT

I have played with XSL and XSLT for a while, and even toyed with the idea of basing my whole website on an XML+stylesheet vision. Because XSLT can get complicated, that idea died before its time. However, I recently found a really good use of XSL on a development project on which I was working. This project included the ubiquitous Log4J library, including a largish logging.xml document. My job was to make sure this document is still relevant. The problem with trying to analyze such a document lies in the way that items reference each other. Instead of pulling my hair out bouncing around in the XML file, I created an XSLT stylesheet to format the whole thing into HTML, with links from loggers to appenders and appenders to loggers. The end result looks like this (broken into 2 pieces for space savings):


logger to appender relationship
appender to logger relationship



The stylesheet that generates this info from the XML document is not long or complex:



<?xml version="1.0"?>
<!-- XSL Stylesheet to make it easier to determine the relationshps between -->
<!-- the loggers and appenders used in the application. -->
<!-- This stylesheet is automatically linked into the logger.xml document. -->
<!-- If you are using any reasonable browser, this stylesheet should be -->
<!-- automatically applied to this file. -->


<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:log4j='http://jakarta.apache.org/log4j/'>

<xsl:template match="/">
<html>
<head>
<title>
Loggers
</title>
</head>
<body>

<xsl:apply-templates select="log4j:configuration" />
</body>
</html>
</xsl:template>

<xsl:template match="log4j:configuration">
<h1>Project Loggers</h1>
<h2>Loggers</h2>
<table border="1">
<tr>
<th>Logger</th>
<th>Priority</th>
<th>Appender(s)</th>
</tr>

<xsl:for-each select="category">
<xsl:sort select="@name" />
<tr>
<td><xsl:value-of select="@name" /></td>
<td><xsl:for-each select="priority" >
<xsl:value-of select="@value" />
</xsl:for-each></td>

<xsl:for-each select="appender-ref">
<td><xsl:value-of select="@ref" /></td>

</xsl:for-each>
</tr>
</xsl:for-each>
</table>
<h2>Appender to Logger Relationship</h2>
<ul>
<xsl:for-each select="appender">
<xsl:sort select="@name" />
<tr>
<li><xsl:value-of select="@name" /></li>
<xsl:for-each select="param">
<xsl:if test="@name = 'File'">
[ <xsl:value-of select="substring-after(@value, '${logging.home}/')" /> ]
</xsl:if>

</xsl:for-each>

<ul>
<xsl:call-template name="show-categories">
<xsl:with-param name="appenderName"><xsl:value-of select="@name" /></xsl:with-param>
</xsl:call-template>
</ul>
</tr>
</xsl:for-each>
</ul>
</xsl:template>

<xsl:template name="show-categories">
<xsl:param name="appenderName" />
<xsl:for-each select="//category">
<xsl:for-each select="appender-ref">
<xsl:if test="@ref=$appenderName">
<li>
<xsl:value-of select="../@name" />
</li>
</xsl:if>
</xsl:for-each>
</xsl:for-each>

</xsl:template>

</xsl:stylesheet>


I also added a reference to this stylesheet in the logger.xml document itself:


<?xml-stylesheet href="ShowLoggerRelationships.xsl" type="text/xsl" ?>


The beauty of this is that any reasonable modern browser will automagically apply this stylesheet anytime you double-click on the XML file. This is the ultimate form of documentation -- generated directly from the source in real time. Any changes to the relationships in the logger.xml document are instantly shown when you look at the file. If you still want to see the raw XML, you right-click and "Open With..." instead. This makes the browser view of the XML document useful without hiding or obscuring the functional elements. And is a good example of the DRY principle (Don't Repeat Yourself) from the Pragmatic Programming applied to documentation.

3 comments:

Anonymous said...

Looks strangely familiar :-).

I never thought of this as documentation, but now that you say that, could we mock up something that could take away the difficulty of reading struts-config.xml files?

-Joe

Neal Ford said...

Absolutely. All the tricks you would need to "beautify" a struts-config document are embodied in this one. Actually, this would work for Hibernate and Spring config files, too.

Check out this quick XSL reference card -- I used this heavily: http://www.mulberrytech.com/quickref/XSLTquickref.pdf

Anonymous said...

Thanks for the quickref link.

I also found that W3Schools XSLT tutorial helps me to quick-start to use XSLT:

http://www.w3schools.com/xsl/default.asp