Adding an XSLT extension

Extend XSLT Functions with C# Code

If you want to execute some C# code from XSLT functions, you can create an XSLT extension object.

What you need to do is:

  1. Write a C# class that represents the extension
  2. Add information about the class to the /App_Data/Composite/Composite.config file
  3. Add a namespace to your XSLT function

As an example, here's a class that has 2 methods:

  • The first method gets a value from a HTTP posted form.
  • The second one does HTML-encoding of a text and converts new line characters "\r\n" into <br/> tags, so line breaks in text will be line breaks in XHTML.

Note that a class should be "public", not-static and should be marked with the necessary attribute, visible methods should be public and non-static.

using System;
using System.Net;
using System.Text;
using System.Web;
using System.Xml.Linq;
using System.Xml.XPath;
using Composite.Plugins.Functions.XslExtensionsProviders.ConfigBasedXslExtensionsProvider;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;

namespace Composite.Examples 
{ 
	[ConfigurationElementType(typeof(ConfigBasedXslExtensionInfo))] 
	public class ExtensionObject 
	{ 
		public string GetFormData(string key) 
		{ 
			var httpContext = HttpContext.Current; 

			if (httpContext == null || httpContext.Request == null || httpContext.Request.Form == null) 
			{ 
				return string.Empty; 
			}

			return httpContext.Request.Form[key] ?? string.Empty; 
		}

		public XPathNavigator ParseLineBreaks(string text) 
		{ 
			string[] parts = text.Split(new string[] {Environment.NewLine}, StringSplitOptions.None);
 
			StringBuilder html = new StringBuilder("<div>"); for (int i = 0; i < parts.Length; i++) 
			{ 
				if (i > 0) 
					html.Append("<br />");

				html.Append(WebUtility.HtmlEncode(parts[i])); 
			}

			html.Append("</div>");
 
			return XElement.Parse(html.ToString()).CreateNavigator(); 
		} 
	} 
}

These are the changes to be made in /App_Data/Composite/Composite.config:

<configuration>
  <!-- skipped -->
  <Composite.Functions.Plugins.XslExtensionsProviderConfiguration>
    <XslExtensionProviders>
      <add name="ConfigurationBasedXslExtensionsProvider" 
           type="Composite.StandardPlugins.Functions.XslExtensions.ConfigBasedXslExtensionsProvider">
        <xslExtensions>
          <!--- skipped -->
          <add name="http://c1.composite.net/Example" 
               type="Composite.Examples.ExtensionObject, App_Code" />
        </xslExtensions>
      </add>
    </XslExtensionProviders>
  </Composite.Functions.Plugins.XslExtensionsProviderConfiguration>
</configuration>

The XSLT function example: 

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:in="http://www.composite.net/ns/transformation/input/1.0" xmlns:e="http://c1.composite.net/Example" xmlns="http://www.w3.org/1999/xhtml" exclude-result-prefixes="xsl in">
  <xsl:template match="/">
    <!-- Using the extension method in order to check if current form was submitted -->
    <xsl:variable name="formPosted" select="e:GetFormData('formId') = 'feedbackForm'" />
    <html>
      <head></head>
      <body>
        <xsl:choose>
          <xsl:when test="not($formPosted)">
            <form method="post">
              <!-- Some markup here -->
              <input type="hidden" name="formId" value="feedbackForm" />
              <textarea name="textArea1" />
              <br />
              <input type="submit" />
            </form>
          </xsl:when>
          <xsl:otherwise>
            Thank you for the participation. <br /> The text you entered: <xsl:copy-of select="e:ParseLineBreaks(e:GetFormData('textArea1'))" />
          </xsl:otherwise>
        </xsl:choose>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

The highlights to the above example:

  1. We specify the namespace ("e") for our extension: xmlns:e="http://c1.composite.net/Example"
  2. We use our first extesion method GetFormData to check if the form has been submitted: <xsl:variable name="formPosted" select="e:GetFormData('formId') = 'feedbackForm'" />
  3. We use second method ParseLineBreaks (on the output of the first method) to correctly present the data the user has entered in the form: <xsl:copy-of select="e:ParseLineBreaks(e:GetFormData('textArea1'))" />