13 May 2008

Creating Views using XSL in ASP.NET MVC

There's something about Web Forms that never felt quite right to me; it all seemed a bit too much like a bodge that created more problems than it solved. I've been having a tinker with ASP.NET MVC for the past few days now and I'm really liking the way it all fits together; giving you clean separation between the layers and a means of passing data between them.

The View layer in MVC uses a very Classic ASP-esque markup for mixing code and HTML. Seems rather dirty but by this point any major processing should have been done and all that you'll need to do is render HTML with maybe some looping.

That being said if all we'll need to do by this point is some looping at the most then why not use an XSL transform instead?

XSL is well defined and established now with a load of tools out there for giving you a WYSIWYG view of your tranform as you build it. Plus as it is purely XML based it will likely be a lot easier for designers (the folk who we really want to build Views) to get to grips with.

A quick proof of concept

Step 1 - Create a model class for your XSL ViewData

This has simply an XmlDocument which will contain our serialized object and a string which will contain a path to our XSL file.

public class XslViewData
{
 private System.Xml.XmlDocument _doc;
 private string _xslPath;

 public XslViewData(System.Xml.XmlDocument doc, string xslPath)
 {
  _doc = doc;
  _xslPath = xslPath;
 }
  
 public System.Xml.XmlDocument Doc
 {
  get 
  {
   return _doc;
  }
 }

 public string XslPath
 {
  get
  {
   return _xslPath;
  }
 }
}

Step 2 - Serialize your object to XML in your Controller

First make sure the type you want to render is ready for XML serialization. This involves adding some attributes to classes and properties so read up on that first before you carry on. It's important to get your type serializing out in a nice way as it will be easier to work with in the XSL.

using System.Xml;
using System.Xml.Serialization;
...
Product prod = ProductRepository.GetProduct(id);
XmlSerializer ser = new XmlSerializer(typeof(Product));  
XmlDocument doc = new XmlDocument();
System.IO.MemoryStream ms = new System.IO.MemoryStream();
XmlWriter xw = XmlWriter.Create(ms);
ser.Serialize(xw, prod);
ms.Position = 0;
doc.Load(XmlReader.Create(ms));
RenderView("Xsl", new XslViewData(doc, "/Content/prodDetail.xsl"));

Step 3 - Create a ViewPage or ViewUserControl to host your XSL

All you need in the aspx/ascx file is an Xml ASP.NET control...

<asp:Xml ID="Xml1" runat="server" onload="Xml1_Load"></asp:Xml>

...and in the code behind...

public partial class Xsl : ViewPage<XslViewData>
{
 protected void Xml1_Load(object sender, EventArgs e)
 {
  Xml1.Document = ViewData.Doc;
  Xml1.TransformSource = ViewData.XslPath;
 }
}

Step 4 - Create an XSL file

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
 
 <xsl:template match="/Product">
  <h2><xsl:value-of select="Name"/></h2>
  <xsl:apply-templates select="Image"/>
    </xsl:template>

 <xsl:template match="Image">
  <img>
   <xsl:attribute name="src">
    <xsl:value-of select="ImageUrl"/>
   </xsl:attribute>
  </img>
 </xsl:template>
 
 <xsl:template match="*">
 </xsl:template>
 
</xsl:stylesheet>

Done

Not only is this simple to implement it has a lot of scope for improvment; compiling transforms, adding caching and configuation etc. This method also has the advantage that it requires no recompile to alter the template.

Links

2 comments:

Tommy said...

Good job!

Michael Hanney said...

Thanks for this XSL transformation example. I had to make a view that returned an XML playlist file for an audio player today. Using an XSL transform to turn the serialized view data into XML was easy and worked first time following your example. Thanks Derek.