XSLT Processing in .NET
August 14, 2002
This article is meant to help XML developers understand the different ways XSLT
transformations can be performed using the .NET framework. It alsos describe how to
use
various input sources for an XSLT transformation. In .NET, the
System.Xml.Xsl.XslTransform
class is used for transforming XML data using an
XSLT stylesheet. System.Xml.Xsl.XslTransform
supports the XSLT 1.0 syntax,
using the http://www.w3.org/1999/XSL/Transform
namespace.
Input and Output Data Sources to the XslTransform
Class
There are several classes that may be used to read XML and XSLT documents for a
transformation. The most versatile of these is the System.Xml.XmlReader
class.
Since System.Xml.XmlReader
is an abstract class, another class must inherit
from it. The first class of this type is System.Xml.XmlTextReader
, which reads
character streams and checks that XML is well-formed, but does not validate the XML
against
a DTD or schema.
The second class that inherits from System.Xml.XmlReader
is
System.Xml.XmlNodeReader
, which allows data to be read from any XML Document
Object Model (DOM) API; for example, System.Xml.XmlNode
. The
System.Xml.XmlNode
does not have to be a complete XML document; it may be a
child node.
The third type of XmlReader
class is
System.Xml.XmlValidatingReader
, a validating reader capable of ensuring XML
data conforms to a DTD, XML-Data Reduced (XDR) schema, or W3C XML Schema.
// Load the String into a TextReader System.IO.TextReader tr = new System.IO.StreamReader("numbers.xsl"); // Use that TextReader as the Source for the XmlTextReader System.Xml.XmlReader xr = new System.Xml.XmlTextReader(tr); // Create a new XslTransform class System.Xml.Xsl.XslTransform trans = new System.Xml.Xsl.XslTransform(); // Load the XmlReader StyleSheet into the XslTransform class trans.Load(xr);
Another type of input class is System.Xml.XPath.XPathNavigator
or any class
that implements the System.Xml.XPath.IXPathNavigable
interface, including
System.Xml.XPath.XPathDocument
, System.Xml.XmlDocument
, and
XMLDataDocument
. The System.Xml.XPath.XPathNavigator
is based on
the XPath data model and provides the methods needed to implement XPath queries over
any
data store.
The System.Xml.XPath.XPathDocument
is the fastest of such classes, because
it's read only, and is the preferred class when the speed of XSLT transformations
is the
highest priority. System.Xml.XmlDocument
is the second most efficient class;
XMLDataDocument
is not recommended for performing XSLT transformations.
System.Xml.XPath.IXPathNavigable
may be implemented against any data source,
allowing any imaginable type of data to be used as the source in an XSLT transformation.
// The following code may be appended to the preceding example // Create a new XPathDocument, loading the source from a file System.Xml.XPath.XPathDocument xp = new System.Xml.XPath.XPathDocument("numbers.xml"); // Create the Navigator System.Xml.XPath.XPathNavigator xpn = xp.CreateNavigator();
The XslTransform
class requires the source of the XML or XSLT data to be an
XmlReader
or System.Xml.XPath.XPathNavigator
. The
Load
and Transform
methods of the
System.Xml.Xsl.XslTransform
class do not provide an overloaded method for
directly processing a Stream
, but the example below demonstrates an indirect
method to use a MemoryStream
as the source location of the XML data and XSLT
stylesheet.
// Create a Stream and show how it would be used as the source // This is only a sample since no data exists in the stream. System.IO.Stream st = new System.IO.MemoryStream(); // You would populate the Stream with the XML File here and set the position to 0 st.Position = 0; // Using the Stream, load it into an XPathDocument System.Xml.XPath.XPathDocument xp = new System.Xml.XPath.XPathDocument(st); // Now do the same with the Xslt document System.IO.Stream XSLTStream = new System.IO.MemoryStream(); // You would populate the Stream with the XSLT File here and set the position to 0 st.Position = 0; // Use an XmlReader and pass in a Stream as the Source System.Xml.XmlReader xsltXR = new System.Xml.XmlTextReader(XSLTStream); // The XmlReader may be passed into the Load Method of XslTransformm
The line containing the initialization of the Stream
is just to demonstrate
the MemoryStream
constructor; it is still necessary to populate the data. One
other source we may wish to use for XML and XSLT documents is a document retrieved
from a
URL, in the form of a string along with an optional System.Xml.XmlResolver
,
which is the most efficient method to load the source from an existing file.
The xsl:output
element is sometimes ignored depending on the type of output
class used; if, for example, the output class is either an XmlWriter
or an
XmlReader
class. For a detailed description of which attributes are supported
on xsl:output
and the classes that support them please read the "Outputs from an XslTransform" section of the MSDN documentation.
The minimum requirements to perform an XSLT transformation are an XML document, an
XSLT
document, and a valid object to handle the output. In the example below, an
XPathDocument
and stylesheet will be loaded from a file, and output directed
to the system console.
// Create the XslTransform. System.Xml.Xsl.XslTransform xslt = new System.Xml.Xsl.XslTransform(); // Load the stylesheet. xslt.Load("numbers.xsl"); // Load the XML data file. System.Xml.XPath.XPathDocument doc = new System.Xml.XPath.XPathDocument("numbers.xml"); // Create the XmlTextWriter to output to the console. System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(System.Console.Out); // Transform the file. xslt.Transform(doc, null, writer); // Be careful when using the console, // closing it will not allow you to write any // additional information to it writer.Close();
XSLT Transformations Involving Streams
It's also possible to output the results of a transformation directly to a
System.IO.Stream
object. In the following example,
System.IO.MemoryStream
will be used for output, and then a
System.IO.StreamReader
used to read the information back as a string and
return the value from the function.
// Create the XslTransform. System.Xml.Xsl.XslTransform xslt = new System.Xml.Xsl.XslTransform(); // Load the stylesheet. xslt.Load("numbers.xsl"); // Load the XML data file. System.Xml.XPath.XPathDocument doc = new System.Xml.XPath.XPathDocument("numbers.xml"); // Create the Stream to place the output. System.IO.Stream str = new System.IO.MemoryStream(); // Transform the file. xslt.Transform(doc, null, str); // Remember to Flush the Stream and set the position to 0 str.Flush(); str.Position = 0;
A System.Xml.XmlDocument
or System.Xml.XPath.XPathDocument
that
is already loaded into memory is also on option for input. Both objects implement
the
System.Xml.XPath.IXPathNavigable
interface, which allows each to be passed as
parameters directly into the Transform
method of the
System.Xml.Xsl.XslTransform
class.
// Create the XslTransform. System.Xml.Xsl.XslTransform xslt = new System.Xml.Xsl.XslTransform(); // Load the stylesheet that creates XML Output. xslt.Load("numbersXML.xsl"); // Load the XML data file into an XPathDocument. System.Xml.XPath.XPathDocument doc = new System.Xml.XPath.XPathDocument("numbers.xml"); // Create the Stream to place the output. System.IO.Stream str = new System.IO.MemoryStream(); System.Xml.XmlWriter xw = new System.Xml.XmlTextWriter(str,System.Text.Encoding.UTF8); // Transform the file. xslt.Transform(doc, null, xw); // Flush the XmlWriter and set the position of the Stream to 0 xw.Flush(); str.Position = 0;
Stylesheet Parameters and Extension Objects
Parameters or extension objects may also be passed to the stylesheet. This is accomplished
using the System.Xml.Xsl.XsltArgumentList
class. Passing a parameter to a
stylesheet gives the programmer the ability to initialize a globally scoped variable,
which
is defined as any xsl:variable
that is a child of the
xsl:stylesheet
and not contained inside a xsl:template
. A
parameter may be added to the System.Xml.Xsl.XsltArgumentList
class by calling
the AddParam
method providing a qualified name, the namespace URI and value. If
the parameter value is not a String, Boolean, Number, Node Fragment
, or
NodeSet
, it will be forced to a double
or string
.
An extension object is any .NET class that provides methods that return an XSLT data
type.
Extension objects are added to the System.Xml.Xsl.XsltArgumentList
using the
AddExtensionObject
method providing a qualified name and namespace URI.
// Using the TextReader, load it into an XPathDocument System.Xml.XPath.XPathDocument xp = new System.Xml.XPath.XPathDocument("numbers.xml"); // Create a new XslTransform class and load the stylesheet System.Xml.Xsl.XslTransform trans = new System.Xml.Xsl.XslTransform(); // Load the XmlReader StyleSheet into the Transformation trans.Load("numbersExtension.xsl"); // Create the XsltArgumentList class and add the // parameter using AddParam System.Xml.Xsl.XsltArgumentList xslArg = new System.Xml.Xsl.XsltArgumentList(); xslArg.AddParam("displayme","","Is this fun?"); // Create and add the extension object using AddExtensionObject SayHello hi = new SayHello(); xslArg.AddExtensionObject("urn:SayHello",hi); // Create the System.IO.Stream to place the output. System.IO.Stream str = new System.IO.MemoryStream(); // Transform the file. trans.Transform(xp, xslArg, str); // Flush the XmlWriter and set the position of the Stream to 0 str.Flush(); str.Position = 0; // Create a StreamReader to Read the Stream and Return the String System.IO.StreamReader sr = new System.IO.StreamReader(str); string xmlOut = sr.ReadToEnd(); // Close the StreamReader and the base Stream sr.Close(); // Write the Results to the Console Console.Write(xmlOut); // Extension Object to return Hello Name public class SayHello { public string HelloName(string name) { return "Hello " + name; } }
There are advantages to using an extension object instead of embedding all the instructions within a script block, including better reuse of the classes and smaller, easier to maintain stylesheets.
Embedding Script or Code in XSLT
Programming and scripting language constructs may also be embedded and utilized in
XSLT
stylesheets by using the msxsl:script
element. The prefix "msxsl
"
is assumed to be bound to the urn:schemas-microsoft-com:xslt
namespace.
Languages supported by the script tag include C#, VB.NET, and JScript.NET, which is
the
default. An implements-prefix
attribute that contains the prefix representing
the namespace associated with the script block must also exist in the
msxsl:script
element. Multiple script blocks may exist in a stylesheet, but
only one language may be used per namespace.
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:msxsl='urn:schemas-microsoft-com:xslt' xmlns:theScript='urn:CustomScript'> <xsl:output omit-xml-declaration='yes' method='text' media-type='text/plain' indent='no' /> <xsl:variable name='displayme' /> <msxsl:script implements-prefix='theScript' language='C#'> <![CDATA[ public string HelloName(string name) { return "Hello " + name; } ]]> </msxsl:script> <xsl:template match='/'> <xsl:text disable-output-escaping='yes'>Print Integers > 3</xsl:text> <xsl:apply-templates select='Root/Numbers' /> Script Result: <xsl:value-of select='theScript:HelloName("Joe")' /> Done: <xsl:value-of select='$displayme' /> </xsl:template> <xsl:template match='Numbers'> Numbers:<xsl:apply-templates select='Integer[@value > 3]' /> </xsl:template> <xsl:template match='Integer'> Integer: <xsl:value-of select='@value' /> </xsl:template> </xsl:stylesheet>
Resources
- Sample code from this article
- W3C XSL Transformations (XSLT) v1.0 Specifications
- W3C XML Path Language (XPath) v1.0 Specifications
- System.Xml.Xsl.XslTransform additional information
- System.Xml.XmlReader additional information
- System.IO.Stream additional information
- Code Samples in C# and VB.NET
- O'Reilly Network .NET DevCenter
- XML.com Column: Transforming XML
Acknowledgments:
I would like to thank Asad Jawahar, Arpan Desai and Praj Joshi for their help in developing this article.