Sign In/My Account | View Cart  
advertisement


Listen Print Discuss

XSLT Processing in .NET
by Joe Feser | Pages: 1, 2

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

Acknowledgments:

I would like to thank Asad Jawahar, Arpan Desai and Praj Joshi for their help in developing this article.


Comment on this articleShare comments or questions on this article in our forum.
(* You must be a
member of XML.com to use this feature.)
Comment on this Article


Titles Only Titles Only Newest First
  • XslTransform.Transform Method doesn't work when xsl contains
    2003-06-18 08:58:59 Tanya Gillingham [Reply]

    The following code works OK with other files, but when xsl contains <msxsl:script> element XslTransform.Transform Method fails when processing JScript functions.
    Any ideas why?


    Many thanks


    C# code:


    string stylesheet = "sample.xsl";
    string xmlfile = "sample.xml";
    XslTransform xslt = new XslTransform();
    xslt.Load(stylesheet);
    XmlDocument xpathdocument = new XmlDocument();
    xpathdocument.Load(xmlfile);
    XmlTextWriter writer = new XmlTextWriter(Console.Out);
    writer.Formatting=Formatting.Indented;
    xslt.Transform(xpathdocument, null, writer, null);



    sample.xml file:


    <catalog>
    <book id="bk101">
    <author>Gambardella, Matthew</author>
    <title>XML Developer's Guide</title>
    <genre>Computer</genre>
    <price>44.95</price>
    <publish_date>2000-10-01</publish_date>
    <description>An in-depth look at creating applications with XML.</description>
    </book>
    <book id="bk102">
    <author>Ralls, Kim</author>
    <title>Midnight Rain</title>
    <genre>Fantasy</genre>
    <price>5.95</price>
    <publish_date>2000-12-16</publish_date>
    <description>A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.</description>
    </book>
    </catalog>


    sample.xsl file:


    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:teletext="teletext.co.uk" version="1.0">


    <msxsl:script language="JScript" implements-prefix="teletext"><![CDATA[
    function daysFromPub(nodelistBook) {
    var dToday = new Date();
    var nodeBook = nodelistBook.item(0);
    var sPublishDate = nodeBook.selectSingleNode("publish_date").text;
    var nYear = Number(sPublishDate.substr(0,4));
    var nMonth = Number(sPublishDate.substr(5,2)) - 1; // months are from 0 to 11
    var nDay = sPublishDate.substr(8,2);

    var MinMilli = 1000 * 60; //Initialize variables.
    var HrMilli = MinMilli * 60;
    var DyMilli = HrMilli * 24;

    var dPublish = new Date(nYear, nMonth, nDay);

    return String(Math.round((dPublish - dToday) / DyMilli));
    }

    ]]> </msxsl:script>
    <xsl:template match="/">
    <html>
    <body>
    <table border="1">
    <tr>
    <td>
    Title
    </td>
    <td>
    Days from publication
    </td>
    </tr>
    <xsl:for-each select="//book">
    <xsl:sort select="teletext:daysFromPub(.)" data-type="number"/>
    <tr>
    <td>
    <xsl:value-of select="title"/>
    </td>
    <td>
    <xsl:value-of select="teletext:daysFromPub(.)"/>
    </td>
    </tr>
    </xsl:for-each>
    </table>
    </body>
    </html>
    </xsl:template>
    </xsl:stylesheet>

    • XslTransform.Transform Method doesn't work when xsl contains
      2005-08-15 07:26:20 shsamuel [Reply]

      Tanya,


      When something fails, it's most useful to see the particular exception it generated. In absence of that, I'll take a guess at what looks like an good reason it might have failed.


      You seem to be missing an XmlResolver and Evidence. The short signature Load and Transform methods have been deprecated (possibly since your original post) to force you to include resolvers and evidence. Resolvers resolve eternal references within your XML, and Evidence gives policy-based rights to process scripts. The documentation is a little sparse, but based on Microsoft's normal ways of doing things, default (i.e. -- null) evidence is probably of the level that will ignore your JScript because the system things it's untrusted.


      If your examples are verbatim and complete, you can probably get away without a resolver: you don't have any external references. Try an explicit System.Security.Policy.Evidence from Assembly.Evidence of your executing assembly (probably easiest as this.GetType().Assembly.Evidence) to give green light security to any code. Bear in mind that this grants any XSL that you run through that code full policy access, so make sure your code won't break anything and comes from a trusted source. Alternately, insist that your source provides evidence and authenticate against that.


  • Using the XmlNavigator
    2002-10-30 08:26:24 Steve Skalski [Reply]

    I used XmlNavigator passing the data set instead of using the stream and filling it with ds.Xmlwrite. It took 0.141 sec to do a transform as opposed to the "messy way" in my first discussion where it took 0.051 sec. Arrgh - 2.76 times slower. Maybe I shouldn't complain about the time.

  • XSL Transforms and .NET DataSets
    2002-10-29 17:33:17 Steve Skalski [Reply]

    I am trying to do XSL transforms that are being returned in ADO DataSets. No real problems performing the transform, however I feel that I am not being efficient in the process. The code is as follows:
    // put xml into memory stream msInput
    Stream msInput,msOutput;
    msInput = new MemoryStream();
    ds.WriteXml(msInput);
    msInput.Position = 0;
    // create Xpath document
    XPathDocument doc = new XPathDocument(msInput);
    // load xsl transform doc
    XslTransform xslt = new XslTransform();
    xslt.Load(sXslFile);
    // transform msInput via xsl into msOutput
    msOutput = new MemoryStream();
    xslt.Transform(doc,null,msOutput);
    msOutput.Flush();
    msOutput.Position = 0;
    // load dataset
    ds = new DataSet();
    ds.ReadXml(msOutput);


    It currently takes about 0.051 sec on a 1000 MHz processor with the majority (circa 97%)of the time going to ReadXml, WriteXml, and Load XSL methods.


    Is it possible to pass the XML directly to XPathDocument without the WriteXml and use of two Memory Streams? Likewise for the ReadXml getting it back into the DataSet?