XSLT Transformation in ASP.net MVC framework
In my last post I talked about using a partial view to render hierarchical data, however XSL transformation looks more appropriate for such cases. ASP.Net has XML control which can display XML document using XSL Transformations. I will show you how similar thing can be achieved in ASP.NET MVC Framework.
The Model
I have a very simple Tab class which can have a collection of sub-tab. this class can be used to create infinite level of tab hierarchy. I have added XmlAttributes to my properties so that when class is serialized those properties are sterilized as attributes.
[XmlAttribute( "id" )]
public int ID { get; set; }
[XmlAttribute( "name" )]
public string Name { get; set; }
[XmlAttribute( "alias" )]
public string Alias { get; set; }
public List<Tab> Tabs { get; set; }
}
The Controller
I have a single controller called TabController it has TabXSLT action, it does two things 1) Gets list of tabs from by calling service and then serializes it to XML and 2) Passes that XML to view.
[ControllerAction]
public void TabXSLT() {
List<Tab> tabs = TabService.GetTabs() ;
XmlSerializer serilizer =
new XmlSerializer( typeof (List<Tab>)) ;
MemoryStream stream = new MemoryStream() ;
stream.Seek( 0 , SeekOrigin.Begin) ;
serilizer.Serialize(stream, tabs) ;
XmlDocument doc = new XmlDocument() ;
stream.Seek( 0 , SeekOrigin.Begin) ;
doc.Load(stream) ;
ViewData[ "tabs" ] = doc ;
RenderView( "TabXSLT" ) ;
}
}
View extension for XSLT
MVC Framework does not have concept of the control, Instead UI Helpers and ViewExtensions will enable you to wrap common functionality. I have used view extension in my case to inject additional method in view which will allow me render transformed XML inside my view.
public static void RenderXML( this ViewPage page
, XmlDocument xmlDocument
, string XSLTPath,
Dictionary< string , string > xslArgParams)
{
ViewContext context = page.Html.ViewContext ;
XsltArgumentList xslArgs = new XsltArgumentList() ;
if (xslArgParams ! = null ) {
foreach ( string key in xslArgParams.Keys) {
xslArgs.AddParam(key, null ,xslArgParams[key]) ;
}
}
XslCompiledTransform t = new XslCompiledTransform() ;
t.Load(XSLTPath) ;
t.Transform(xmlDocument, xslArgs,
context.HttpContext.Response.Output) ;
}
}
The View
I have the TabXSLT.aspx view in /Views/Tab folder. Inside my view I am calling extension method by passing XML data that I got from controller and path to XSLT file.
Inherits ="System.Web.Mvc.ViewPage"
MasterPageFile ="~/Views/Shared/Site.Master" % >
< %@ Import Namespace ="ExperimentsWithMVC.Models" % >
< %@ Import Namespace ="System.Collections.Generic" % >
< %@ Import Namespace ="System.Web.Mvc" % >
< %@ Import Namespace ="System.Xml" % >
< asp:Content ID ="Content2"
ContentPlaceHolderID ="MainContentPlaceHolder"
runat ="server">
< h2 > Tabs with XSLT </ h2 >
< % this.RenderXML((XmlDocument)ViewData[ "tabs"]
, Server.MapPath( "~/content/tabs.xsl"),null);%>
</ asp:Content >
The XSLT
< xsl:stylesheet version ="1.0"
xmlns:xsl ="http://www.w3.org/1999/XSL/Transform" >
< xsl:output omit-xml-declaration ="yes" />
< xsl:template match ="/">
< xsl:apply-templates > </ xsl:apply-templates >
</ xsl:template >
< xsl:template match ="Tab">
< li >
< xsl:value-of select ="@name"/>
< xsl:apply-templates select ="Tabs"> </ xsl:apply-templates >
</ li >
</ xsl:template >
< xsl:template match ="Tabs">
< ul >
< xsl:apply-templates select ="Tab" />
</ ul >
</ xsl:template >
< xsl:template match ="ArrayOfTab">
< ul >
< xsl:apply-templates select ="Tab" />
</ ul >
</ xsl:template >
</ xsl:stylesheet >
The result
Result is nested unordered list as shown below, each item can have sub item up to nth level.
Parting Thoughts
I have used extension method to inject additional method in my current ViewPage class but it might be more appropriate to group similar UI helper methods in static class, for example you can have AJAXHelper, HTMLHelper, XMLHelper etc.