XSLT Transformation in ASP.net MVC framework
Saturday, February 9, 2008
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.
public class
Tab {
[XmlAttribute(
"id"
)]
public int
ID {
get; set;
}
[XmlAttribute(
"name"
)]
public string
Name {
get; set;
}
[XmlAttribute(
"alias"
)]
public string
Alias {
get; set;
}
public
List 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.
public class
TabController : Controller {
[ControllerAction]
public void
TabXSLT() {
List tabs
=
TabService.GetTabs()
;
XmlSerializer serilizer
=
new
XmlSerializer(
typeof
(List))
;
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 class
XSLTViewExtensions {
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.
<
%@
Page Language
="C#"
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
<
?xml
version
="1.0"
encoding
="utf-8"?>
<
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.