XslCompiledTransform (new XSLT 1.0 processor in .NET 2.0) - no more pull-mode XSLT

| 8 Comments | No TrackBacks

I'm studying new XSLT 1.0 implementation provided by Microsoft in the .NET 2.0 Beta2 - XslCompiledTransform class. The guys who wrote it are my good friends and excellent developers, but let me to complain a little bit, not because I'm a complainer, but trying to make this cool piece of software even better.

XslTransform class, now obsoleted in .NET 2.0 has a very nice feature - it allows to perform pull-mode XSL transformations. The result of the transformation can be an XmlReader object and the actual transformation occurs only when you call that reader methods. That feature, traditionally overlooked and rarely mentioned (come on, who cares about perf nowadays, right?) enables very efficient XML pipelining with no temporary XML stores. If a component (such as SqlXml class) accepts XML as an instance of XmlReader and you need to feed it with XSLT output that is easy and efficient. I was trying to promote this feature with XmlTransformingReader class, which you can find in the Mvp.Xml library.

Now, new and shiny XslCompiledTransform class doesn't support this feature anymore, transformation output is now only Stream, TextWriter or XmlWriter, so it's pure push-mode transformer. That actually simplified API a bit, but now in a scenario when you need a transformation result as an XmlReader one needs 1) a temporary XML storage and 2) write nasty complicated code like this:

// Load XSL transformation
XslCompiledTransform xform = new XslCompiledTransform();
xform.Load (xslPath);
// Execute/Cache results
XmlDocument resultsDoc = new XmlDocument();
XPathNavigator resultsNav = resultsDoc.CreateNavigator();
// using makes sure that we flush the writer at the end
using (XmlWriter writer = resultsNav.AppendChild()) 
{
  xform.Transform(XmlData.CreateReader(), writer);
}
// Return results
SqlXml retSqlXml = new SqlXml (resultsNav.ReadSubtree());
return (retSqlXml);
It's from Michael Rys's excellent webcast on "Managing XML Data on the Database with SQL Server 2005 and Visual Studio 2005". Temporary XML document, what can be worse? In .NET 1.X this could be done much easier and efficient:
// Load XSL transformation
XslTransform xform = new XslTransform();
xform.Load (xslPath);

//Load source XML
XPathDocument doc = new XPathDocument(XmlData.CreateReader());

// Return results
SqlXml retSqlXml = new SqlXml (
  xform.Transform(doc, null, new XmlUrlResolver()));
return (retSqlXml);

Now that's a pity .NET 2.0 doesn't add any new XSLT-related functionality, but even cuts off some :( I wonder if this was cut because it was unfeasible to implement with new XslCompiledTransform architecture or the reason was a different one. Oh well, I filed this issue as a bug. Go vote for it if you care.

Related Blog Posts

No TrackBacks

TrackBack URL: http://www.tkachenko.com/cgi-bin/mt-tb.cgi/423

8 Comments

Thank you. I'm no happier but I feel much saner. I will report it on MSDN.
Kaylee

Yeah, I get 875 ms compilation time. Is it too much for your application? If so, file a bug at MSDN feedback center, I believe compilation can be done a bit faster.

I'm lost...

The C# code (I know it's convoluted but I will eventually be getting BOTH the xml and xsl as strings):

public static void Main()
{

int start, stop;

//get the xsl as a string
StreamReader xslr = new StreamReader(stylesheet);
string xslStr = xslr.ReadToEnd();
Console.WriteLine(xslStr);

//get the xml as a string
StreamReader sr = new StreamReader(filename);
string inStr = sr.ReadToEnd();

start = Environment.TickCount;
// Load the style sheet.
StringReader xslReader = new StringReader(xslStr);
XmlReader xslRdr = XmlReader.Create(xslReader);
stop = Environment.TickCount;
Console.WriteLine("\nXSLLoad time in ms: {0}",(stop - start).ToString());

start = Environment.TickCount;
XslCompiledTransform xslt = new XslCompiledTransform();
stop = Environment.TickCount;
Console.WriteLine("\nCTCreate time in ms: {0}",(stop - start).ToString());

start = Environment.TickCount;
xslt.Load(xslRdr);
stop = Environment.TickCount;
Console.WriteLine("\nCTLoad time in ms: {0}",(stop - start).ToString());

start = Environment.TickCount;

//Load the xml
StringReader strReader = new StringReader(inStr);
StringWriter strWriter = new StringWriter();
XmlReader reader = XmlReader.Create(strReader);
stop = Environment.TickCount;
Console.WriteLine("\nXmlLoad time in ms: {0}",(stop - start).ToString());

start = Environment.TickCount;
// Transform
xslt.Transform(reader, null, strWriter);
stop = Environment.TickCount;
Console.WriteLine("\nTransform time in ms: {0}",(stop - start).ToString());

}

and here's the little stylesheet that's causing the problems (again, convoluted, because of all the testing):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<RESULTS>
<xsl:apply-templates select="node()"/>
</RESULTS>
</xsl:template>
<xsl:template match="ITEM">
<xsl:if test="boolean(./CUSIP='037023108')">
<xsl:copy-of select='.' />
</xsl:if>
</xsl:template>

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

</xsl:stylesheet>


Thanks,
Kaylee

Make sure you are not running in debug mode (bool argument to the XslCompiledTransform constructor).
Can you send the code me so I could try to reproduce the problem?

It's a very simple stylesheet that I developed specifically for testing. It has a couple of templates. One handles the generic case (node()) but does nothing except apply-templates. The other is looking for a specific case and will do a copy-of that node if found.
The relevant portions of the code were taking less than a half second on 1.1. The XslCompiledTransform.load() (with xmlReader as an arg) alone is taking 1283 ms on 2.0. I've checked it several times because I was incredulous.

Well, it compiles XSLT down to MSIL and assembly, but it should't be deadly slow. What kindof stylesheet and how you run?

I have been doing some performance testing. I am comparing XSL transform performance between Framework 1.1 and 2.0. I must be doing something wrong. I am getting some huge numbers when doing a XslCompiledTransform.load(). I realize that it is doing extra work but the load is killing me. Any suggestions?

This sucks. I've only just mastered (to some kind of beginner level) XslTransform, thanks mainly to Oleg and Daniel Cazzulino's writings (blogs and MVP.XML).

The annoying part is the MSDN page on how to migrate from XslTransform to XslCompiledTransform convieniently ignores the overloads of transform that return the XmlReader.

http://winfx.msdn.microsoft.com/library/default.asp?url=/library/en-us/wd_xml/html/9404d758-679f-4ffb-995d-3d07d817659e.asp

With Beta 2 out, what are the chances of this being changed back before the final version?

Leave a comment