April 18, 2007

HOW TO: Pipeline XSLT Transformations in .NET 2.0 Applications

I mean pre-XProc XSLT pipeline - just when you need to transform an XML document by a sequence of XSLT stylesheets - output from the first transformation goes as input to the second one and so on. This is useful technique helping dramatically simplify your complex multi-stage XSLT stylesheets. Unfortunately there is no simple way to perform such task effectively in .NET 2.0. Here I show you how Mvp.Xml project comes to the rescue.

Note: there is old crappy Microsoft KB article 320847 suggesting pipelining XSLT via byte buffer, Bleh!, don't do this.

The problem is that while the most efficient (fastest, taking less memory) XML store for doing XSLT transformations in .NET 2.0 is still old good XPathDocument (not surprisingly specially designed and optimized for XPath and XSLT), there is no way to take XSLT output directly to XPathDocument. XslCompiledTransform doesn't provide XmlReader over its output. It can do XmWriter, but XPathDocument cannot be loaded via XmlWriter (this is likely to be fixed in post Orcas .NET version).

The problem was solved though. Sergey Dubinets from the Microsoft XML Team contributed his excellent XslReader implementation, which provides an efficient way to read XSLT results as XmlReader. I later wrapped it all into MvpXslTransform class, which extends capabilities of the XslCompiledTransform class by adding support for transforming into XmlReader , vast collection of EXSLT extension functions, multiple outputs and transforming of IXPathNavigable along with XmlResolver.

Here is finally code sample that says it all:

using System;
using System.Xml.Xsl;
using System.Xml.XPath;
using System.Xml;
using Mvp.Xml.Common.Xsl;

class Program
{
    public static void Main()
    {
        MvpXslTransform xslt1 = new MvpXslTransform();        
        xslt1.Load("../../XSLTFile1.xslt");
        MvpXslTransform xslt2 = new MvpXslTransform();
        xslt2.Load("../../XSLTFile2.xslt");
        XPathDocument doc = new XPathDocument("../../source.xml");
        XmlReader stage1Output = xslt1.Transform(new XmlInput(doc), null);
        xslt2.Transform(new XmlInput(stage1Output), null, 
            new XmlOutput(Console.Out));        
    }
}

 Simple, fast and memory effective. Get Mvp.Xml library here.

...