0 Comments

I recently had need to create some HTML output from a .NET console application. Often in this scenario, I will simply crank out the HTML in code, constructing it bit by bit with a StringBuilder. However, this time round I decided to look for a more elegant solution. I wanted to create a text file with a template, and for my data to be dynamically put into the right place.

While this could be done with XLST, or even some custom string replacement code, I decided to try out a .NET templating engine. There are a number of these available, including NHaml, Brail, and Spark, but I chose to go with NVelocity, whose syntax seemed to be nice and straightforward, allowing other developers to easily see what is going on and make changes to the templates.

Getting the NVelocity DLL

This proved harder than I was expecting. The original NVelocity project has not been updated in several years, but over at the Castle Project they have taken the source and are improving it. However, I couldn’t find a Castle Project download that contained a built DLL, so I ended up having to download the entire Castle Project source using Subversion, and building it.

Creating a Template

This is the nice and easy bit. Here you can see I am printing out a HTML table of books in a collection of books. I think the NVelocity syntax is pretty self-explanatory.

<h3>Books</h3>

#foreach($book in $books)
#beforeall
<table>
  <tr>
    <th>Title</th>
    <th>Author</th>
  </tr>
#before
  <tr>
#each
    <td>$book.Title</td>
    <td>$book.Author</td>
#after
  </tr>
#afterall
</table>
#nodata
No books found.
#end

Applying the Transformation

Now we need to get hold of the template we created and load it into a stream. I embedded my template as a resource. Then we need to set up a VelocityContext, which will contain all the data needed to be injected into our HTML. Then it is a simple matter of creating the VelocityEngine and passing it the context and the template. It returns a string, which can be written to disk if required.

public static string TransformBooksToHtml(IEnumerable<Book> books, string resourceTemplateName)
{
    Stream templateStream = typeof(TemplateEngine).Assembly.GetManifestResourceStream(resourceTemplateName);
    var context = new VelocityContext();
    context.Put("books", books);
    return ApplyTemplate(templateStream, context);
}

public static string ApplyTemplate(Stream templateStream, VelocityContext context)
{
    VelocityEngine velocity = new VelocityEngine();
    ExtendedProperties props = new ExtendedProperties();
    velocity.Init(props);
    var writer = new StringWriter();
    velocity.Evaluate(context, writer, "XYZ", new StreamReader(templateStream));
    return writer.GetStringBuilder().ToString();
}

Limitations

One limitation that comes to mind is that I am not sure what would happen if the data contained characters that needed to be encoded for HTML (e.g. the less than symbol). I haven’t tested this scenario, but I am sure there is some way of working round it (especially as NVelocity is intended specifically for scenarios requiring HTML output).

Vote on HN

Comments

Comment by DFELK

Mark,

If you really want some super fluid syntax, check out the Spark View Engine. It can also be used as a template engine as well.

www.sparkviewengine.com

Donn

Comment by Mark H

It's one of the ones I had a brief look at. I'd like to try it out properly. I needed the syntax to be friendly to people who were just modifying existing templates, without knowing anything about the templating engine. I thought the NVelocity syntax was nice for that.

Comment by rich

Hi Mark,

Did you manage to build Castle OK using Nant and VS2008 by any chance? Just wondering if you had any problems with not being able to use the .NET v3.5 framework? Trying in vain to extract NVelocity out of the Castle framework but failing miserably!

Cheers :-)

Rich

Comment by Mark H

hi rich, I had to download the entire huge repository of code from subversion, but I found a batch file that built everything in the end. I was using a machine with VS2008 installed.

comments powered by Disqus