Welcome to MSDN Blogs Sign in | Join | Help

Refactoring XSLT

I guess a quick introduction wouldn't be completely out of order for a first post. My name is Don Smith and I've been a Dev Consultant at Microsoft in Charlotte, NC for just over 5 years now. I spend most of my time helping our customers to be successful with Microsoft's XML technologies (XSLT, XSD, etc.), Web Services, and distributed architectures. I've been blogging for several years, but I'm just now deciding to join the MSDN blog party. Would love to hear from you if you feel so moved. :-)

Refactoring XSLT is a concept that originally dawned on me while delivering an XML development workshop earlier this year. Here's a touch of background. According to Martin Fowler, Refactoring is improving the design of existing code. With that in mind, one of the most common uses of XSLT is to transform XML data into HTML. Many enterprise web sites are divided [roughly] into 3 sections: the header, the main content, and the footer. If you've even seen or developed an XSLT stylesheet that results in HTML and has a considerable amount of code in the header and footer sections (or perhaps a non-trivial navigation system) you know the spaggetti code (mixture of code and HTML) required to get there is remnicient of Classic ASP. I'm not proposing there is a way to completely avoid the XSLT/HTML coupling, but this method can trim up your XSLT code and apply some structure to your solution.

The principles behind this approach are based on stylesheet extensibility (<xsl:include />) and factoring out large parts of the HTML into their own files. To illustrate this concept I'll use the following files:

  • Data.xml - A list of our fund raising volunteers and the amount of money they raised.
  • Content.xslt - The main stylesheet. It includes Common.xslt, Header.xslt, and Footer.xslt.
  • Common.xslt - A stylesheet that contains a script and template callable from other stylesheets.
  • Header.xslt - The stylesheet responsible for outputting the header HTML.
  • Footer.xslt - The stylesheet responsible for outputting the footer HTML.
  • Header.txt - A text file containing the HTML for the header section.
  • Footer.txt - A text file containing the HTML for the footer section.
  • Config.xml - A file that contains the locations of the header and footer files.

Below are enough files to get the idea of how this approach works ...

Data.xml - nothing special here; just a simple XML document

<?xml version="1.0" encoding="utf-8" ?> 
<contributors xmlns="urn:fund-raiser">
  <volunteer>
    <name>Joe Blow</name>
    <amount>58.25</amount>
  </volunteer>
  <volunteer>
    <name>John Doe</name>
    <amount>81.50</amount>
  </volunteer>
  <volunteer>
    <name>Jane Blow</name>
    <amount>89.75</amount>
  </volunteer>
  <volunteer>
    <name>Julie Doe</name>
    <amount>96.00</amount>
  </volunteer>
</contributors>

Content.xslt - as you can see, this file's sole function is to produce the main conent for the page (the table). Its call to the Header template will resolve to the Header.xslt include and likewise for the footer. The Common.xslt isn't used by anything in this file. Rather, it will be used by the header and footer (or any other child) stylesheets.

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:v="urn:fund-raiser"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  
  <xsl:include href="Common.xslt" />
  <xsl:include href="Header.xslt" />
  <xsl:include href="Footer.xslt" />

  <xsl:template match="/">
    <xsl:call-template name="Header" />
    <table width="250">
      <xsl:apply-templates select="v:contributors/v:volunteer"/>
    </table>
    <xsl:call-template name="Footer" />
  </xsl:template>

  <xsl:template match="v:volunteer">
    <tr>
      <td width="50%"><xsl:value-of select="v:name" /></td>
      <td width="50%"><xsl:value-of select="v:amount" /></td>
    </tr>
  </xsl:template>
  
</xsl:stylesheet>

Header.xslt - all of the header HTML data is in an external file defined in Config.xml. The path is retrieved and passed into the FileContents template as a parameter. The FileContents template will resolve to Common.xslt.

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template name="Header">

  <xsl:variable name="headerfile" 
    select="document('Config.xml')/configuration/header" />

  <xsl:call-template name="FileContents">
    <xsl:with-param name="filepath" select="$headerfile" />
  </xsl:call-template>

</xsl:template>

</xsl:stylesheet>

Common.xslt - this is the only part of the solution I'm not crazy about: the script. Granted, even if you're not using .NET, most popular XSLT implementations have similar extension facilities. As you can see below, the FileContents template calls the GetContents function to retrieve the contents of the header. The XPath document() function can't be used here because the header and footer files don't contain well-formed markup.

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"

  xmlns:fac="urn:factoring-xslt"
  xmlns:msx="urn:schemas-microsoft-com:xslt"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  
  <msx:script language="c#" implements-prefix="fac">

    public string GetContents( string filepath )
    {
      try
      {
        using( System.IO.TextReader reader = 
        System.IO.File.OpenText( filepath ) )
        {
          return reader.ReadToEnd();
        }
      }
      catch( System.Exception ex )
      {
        return ex.Message;
      }
    }

  </msx:script>

  <xsl:template name="FileContents">

    <xsl:param name="filepath" />
    <xsl:value-of select="fac:GetContents( $filepath )" 
      disable-output-escaping="yes" />

  </xsl:template>

</xsl:stylesheet>

If you want to play around with these files, feel free to download my sample solution here. I've never seen this approach used anywhere and I've never seen anyone else mention it. I thought a purely original post would be a good start on MSDN. So, if you decide to implement any of these ideas, please test thoroughly. I can't stress that enough. Here are some other resources you might find useful:

If you have any thoughts about this stuff, I'd love to hear 'em. Happy coding!

Published Friday, November 26, 2004 6:06 PM by donsmith
Filed under:

Comments

# Good Refactoring article by Don Smith of Dev4Net fame

Refactoring XSLT This is mostly .NET specific but worth the read none the less. Just for fun heres a picture of Don at the XML DevCon with Becky Dias and company......
Saturday, November 27, 2004 3:56 AM by <XSLT:Blog />

# re: Refactoring XSLT

XSLT Transformations in the .NET Framework is not supported so well :(
Saturday, November 27, 2004 10:37 AM by Eric Wang

# Two Web Service

Saturday, November 27, 2004 6:42 PM by Don Smith

# re: Refactoring XSLT

Eric, please elaborate on exactly what aspect of XSLT isn't supported so well. I think the .NET Framework has fantastic support XSLT. Heck, it has an entire namespace dedicated to it (System.Xml.Xsl). I will admit the transformations aren't quite as fast as our MSXML COM implementation, but it's getting faster with every release. Even with that said, it's fast enough now for most applications. Looking forward to your response.
Sunday, November 28, 2004 10:11 AM by Don Smith

# Two Web Service

Monday, November 29, 2004 3:11 PM by Don Smith (dev4net)

# re: Refactoring XSLT

To Don,

Yes, I complained about the speed. Our team have developed a project prototype which use the System.Xml.Xsl, but the speed is not accepted. We have to give it up finally.

Well, I hope Microsoft will put more optimism effort on it. And I believe you will.
Saturday, December 04, 2004 10:58 AM by Eric Wang

# re: Refactoring XSLT

Thursday, May 12, 2005 2:27 AM by Qflash
New Comments to this post are disabled
 
Page view tracker