April 17, 2005

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

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.

April 17, 2005 7:26 PM | #System.Xml v2.0
Comments

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

Posted by: Kaylee Engellenner at August 29, 2005 3:38 PM

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.

Posted by: Oleg Tkachenko at August 28, 2005 5:57 PM

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

Posted by: Kaylee Engellenner at August 25, 2005 12:32 AM

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?

Posted by: Oleg Tkachenko at August 22, 2005 10:34 AM

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.

Posted by: Kaylee Engellenner at August 19, 2005 2:48 PM

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

Posted by: Oleg Tkachenko at August 18, 2005 10:49 PM

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?

Posted by: Kaylee Engellenner at August 18, 2005 7:07 PM

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?

Posted by: Bonfirst at April 21, 2005 7:11 AM
Post a comment




Remember Me?