News


Using Xslt along with XPath we can transform any xml document in the way we want and use it e.g. to create the html page. At this time, we have Xslt 2.0 and XPath 2, but unfortunatelly .Net Framework doesn't support them. We can only use Xslt 1.

We have two ways to work with Xslt 2. One is to use a 3rd party library, e.g. XQSharp or Saxon. The second option is to manually implement the missed in Xslt 1 functions. How to do it I want to show in this post.

 

First is the sample code. The goal is to create a table from this xml data:

<?xml version="1.0" encoding="utf-8"  ?>
<books>
  <book isAvailable="true" averageUsersRating="4.0">
    <title>First title</title>
    <author>First book author</author>
  </book>
  <book isAvailable="true" averageUsersRating="1.2">
    <title>Second title</title>
    <author>Second book author</author>
  </book>
  <book isAvailable="false" averageUsersRating="5.0">
    <title>Third title</title>
    <author>Third book author</author>
  </book>
</books>

The books which are currently not available must be placed in red table row. And the books titles which have rating greater that 4 must be written in upper case.

Xsl to transform this data to html table looks as follow

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
  <xsl:output method="html" indent="yes"/>

  <xsl:template match="/">
    <html>
      <head></head>
      <body>
        <table>
          <tr>
            <th>Title</th>
            <th>Author</th>
            <th>Average rating</th>
          </tr>

          <xsl:for-each select="books/book">
            <xsl:choose>
              <xsl:when test="@isAvailable='true'">
                <tr class="bookIsAvailable">
                  <xsl:call-template name="bookRowTemplate"/>
                </tr>
              </xsl:when>
              <xsl:otherwise>
                <tr class="bookIsNotAvailable">
                  <xsl:call-template name="bookRowTemplate"/>
                </tr>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:for-each>

        </table>
      </body>
    </html>
  </xsl:template>

  <xsl:template name="bookRowTemplate" >
    <td>
      <xsl:choose>
        <xsl:when test="@averageUsersRating &gt; 4">
          <!-- return title in upper case -->
        </xsl:when>
        <xsl:otherwise>
           <xsl:value-of select ="./title"/>
        </xsl:otherwise>
      </xsl:choose>        
    </td>
    <td>
      <xsl:value-of select ="./author"/>
    </td>
    <td>
      <xsl:value-of select ="@averageUsersRating"/>
    </td>
  </xsl:template>
</xsl:stylesheet>

This stylesheet is I think self-explanatory. Colouring the appropriate table rows is easy epending on the value of isAvailable attribute.

The second requirement is to write the appropriate books titles in upper case. Xslt/XPath doesn't have (or I don't know) function for that, so we must write it manually.

In C# function this is easy to write:

    public class XslStringExtensions
    {
        public string ConvertToUpperCase(string source)
        {
            return source.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
        }
    }

But is it possible to use this C# method in the stylesheet? The answer is yes.

First we must add to the stylesheet new attribute: xmlns:StringExtensions="urn:StringExtensions". Now we can write in the xsl the code for executing this method:

        <xsl:when test="@averageUsersRating &gt; 4">
          <xsl:value-of select ="StringExtensions:ConvertToUpperCase(./title)"/>
        </xsl:when>

At the end, we must configure the XslCompiledTransform to use the custom C# function. To do that, we must create list with arguments:

           XsltArgumentList args = new XsltArgumentList();
           args.AddExtensionObject("urn:StringExtensions", new XslStringExtensions());'

And that list we must pass to the XslCompiledTransform.Transform object, e.g.

            XslCompiledTransform transform = new XslCompiledTransform();
            transform.Load(xslTemplate);
            XmlDocument doc=loadedXmlDoc;
            MemoryStream returnedHtml = new MemoryStream();
            transform.Transform(doc.CreateNavigator(), args, returnedHtml);

Comments have been closed on this topic.