Tuesday, 7 June 2011

C# MVC 3 - Render XSLT using a HTMLHelper Extension


This is an example on how to transform XML documents using XSLT, within C# MVC using a HTMLHelper extension and Razor View Syntax.


Method of operation
1. View is requested
2. Controller is invoked
3. Controller finds the location of the XML and XSL files for the current request. Parameters are also defined here.
4. The view is created and returned by the controller
5. The view invokes the HTMLExtension method (Razor View Syntax) to render the XML (RenderXSLT method).
6. The extension transforms the document and returns the result.
7. The transformation is displayed within the view.


Controller Method
        public ActionResult Index()
        {
            XsltViewModel model = new XsltViewModel()
            {
                XmlPath = Server.MapPath("~/PathToXMLFileHere.xslt")
                XsltPath = Server.MapPath("~/PathToXSLTFileHere.xslt")
            };
 
            return View(model);
        }



Code within the view (calls out extension method using the model info)
@model RenderXSLTExample.XsltViewModel
 
<div class="xmlResult">
    
    @Html.RenderXslt(Model.XsltPath, Model.XmlPath)
        
</section>



XsltViewModel object (holds reference to XML, XSLT and params)
// <copyright file="XsltViewModel.cs" company="GinkoSolutions">
// Copyright (c) 2011 All Right Reserved
// </copyright>
// <author>Sean Greasley</author>
// <email>sean@ginkosolutions.com</email>
// <summary>Manages XSLT and XML information model objects</summary>
namespace RenderXSLTExample
{
    using System.Collections.Generic;
 
    /// <summary>
    /// Manages XSLT and XML information model objects
    /// </summary>
    public class XsltViewModel
    {
        /// <summary>
        /// Holds a reference to the XSLT path
        /// </summary>
        public string XsltPath { get; set; }
 
        /// <summary>
        /// Holds a reference to the XML path
        /// </summary>
        public string XmlPath { get; set; }
 
        /// <summary>
        /// Optional parameters to pass into the XSL file.
        /// </summary>
        public List<KeyValuePair<string, string>> Params { get; set; }
    }
}



HTMLHelper Extension class
// <copyright file="HtmlHelperExtensions.cs" company="GinkoSolutions">
// Copyright (c) 2011 All Right Reserved
// </copyright>
// <author>Sean Greasley</author>
// <email>sean@ginkosolutions.com</email>
// <summary>A HTMLExtension method to render XML using XSL</summary>
namespace RenderXSLTExample
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Web;
    using System.Web.Mvc;
    using System.Xml;
    using System.Xml.Resolvers;
    using System.Xml.Xsl;
    using System.Xml.XPath;
 
    /// <summary>
    /// A HTMLExtension method to render XML using XSL
    /// </summary>
    public static class HtmlHelperExtensions
    {
        /// <summary>
        /// Accepts a reference to the XML and XSL files and applies a transformation with optional
        /// parameters.
        /// </summary>
        /// <param name="helper">A reference to the HtmlHelper object</param>
        /// <param name="xsltPath">The path to the XSL file</param>
        /// <param name="xmlPath">The path to the XML file</param>
        /// <param name="parameters">Optional: A list  of arguments to pass into the XSL file</param>
        /// <returns>MvcHtmlString representing the transform</returns>
        public static MvcHtmlString RenderXslt(this HtmlHelper helper, string xslPath, string xmlPath, List<KeyValuePair<string, string>> parameters = null)
        {
            string xsltResult = string.Empty;
 
            try
            {
                // XML Settings
                XmlReaderSettings xmlSettings = new XmlReaderSettings();
                xmlSettings.XmlResolver = null;
                xmlSettings.IgnoreComments = true;
                xmlSettings.DtdProcessing = DtdProcessing.Ignore;
                xmlSettings.ValidationType = ValidationType.None;
 
                // Attaches an action to the valiation event handler. This will write out error messages in the Output pane.
                #if DEBUG
                xmlSettings.ValidationEventHandler += (sender, e) =>
                {
                    Debug.WriteLine(string.Format("{0}({1},{2}): {3} - {4}", e.Exception.SourceUri, e.Exception.LineNumber, e.Exception.LinePosition, e.Severity, e.Message));
                };
                #endif
 
                // XSLT Settings
                XmlReaderSettings xsltSettings = new XmlReaderSettings();
                xsltSettings.XmlResolver = null;
                xsltSettings.DtdProcessing = DtdProcessing.Ignore;
                xsltSettings.ValidationType = ValidationType.None;
 
                // Attaches an action to the valiation event handler. This will write out error messages in the Output pane.
                #if DEBUG
                xsltSettings.ValidationEventHandler += (sender, e) =>
                {
                    Debug.WriteLine(string.Format("{0}({1},{2}): {3} - {4}", e.Exception.SourceUri, e.Exception.LineNumber, e.Exception.LinePosition, e.Severity, e.Message));
                };
                #endif
 
                // Init params
                XsltArgumentList xslArgs = new XsltArgumentList();
                if (parameters != null)
                {
                    foreach (KeyValuePair<string, string> param in parameters)
                        xslArgs.AddParam(param.Key, string.Empty, param.Value);
                }
 
                // Load XML
                using (XmlReader reader = XmlReader.Create(xmlPath, settings))
                {
                    // Load XSL
                    XsltSettings xslSettings = new XsltSettings(true, true); // Need to enable the document() fucntion
 
                    using (XmlReader xslSource = XmlReader.Create(xslPath, xsltSettings))
                    {
                        XslCompiledTransform xsltDoc = new XslCompiledTransform();
                        xsltDoc.Load(xslSource, xslSettings, new XmlUrlResolver());
 
                        // Transform
                        using (var sw = new UTF8StringWriter())
                        {
                            XmlWriterSettings settings = new XmlWriterSettings();
                            settings.Encoding = Encoding.UTF8;
                            settings.OmitXmlDeclaration = true;
 
                            using (var xw = XmlWriter.Create(sw, settings))
                            {
                                xsltDoc.Transform(reader, xslArgs, sw);
                            }
 
                            xsltResult = sw.ToString();
                        }
                    }
                }
            }
            catch { } // custom error handling here
 
            // Return result
            return MvcHtmlString.Create(xsltResult);
        }
    }
}

11 comments:

NETIONS said...

There was an error "using (var sw = new UTF8StringWriter ())"... UTF8String does not exist. Another error and another error occurred in "using (XmlReader reader = XmlReader.Create (xmlPath, settings))" where settings is not. Could you explain the error.

s34nvideos said...

Can you post a full descrption of both errors?

It sounds like something hasn;t been configured properly with regards to either your: Path to xml file or xml settings.

Sean

Young said...

Hi, line: XmlReader.Create(xmlPath, settings)) shows that "the name 'setting' does not exist in the current context" and UTF8StringWriter error shows: "the type or namespace name 'UTF8StringWriter' could not be found..."
I think it's missing some reference somewhere

sebastien said...

Got the same problem her.

sebastien said...
This comment has been removed by the author.
sebastien said...
This comment has been removed by the author.
Jeff said...

"settings" is supposed to be "xmlSettings" and the UTF8StringWriter is below.

public class UTF8StringWriter : StringWriter
{
public override Encoding Encoding
{
get { return Encoding.UTF8; }
}
}

Rutu said...

This blog is realy helpfull Can you please provide xml and xslt being used in above code

Unknown said...

How could you call the RenderXslt method from the view?
@Html.RenderXslt(Model.XsltPath, Model.XmlPath)

Anonymous said...

Almost at the edge, but this code doesn't work. I have errors with utf. it says no namespace encoding could be found.

it would be great if you could please repost the entire code, without errors.

Anonymous said...
This comment has been removed by the author.