Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Text Template Class for C#

0.00/5 (No votes)
9 Dec 2004 2  
A simple Text Template class for .NET using a RegEx callback function.

Introduction

A tag based template system can be implemented in C# using the MatchEvaluator delegate of the RegEx.Replace() method. The tags are stored in a hashtable with the key being the tag itself and the replacement text is the Value of a TemplateTag object. The code for manipulating the hashtable and for parsing the template is contained in a single class, TemplateParser.

Background

Having used ASPTemplate extensively for ASP development and code generation, I naturally started looking for something similar for C#. During my search, I came across the article Templates in Ruby, by Jack Herrington, where a combination of Regular Expressions and a hashtable were used to build a simple template system. Since I could not find any other C# based tag templating systems that met my needs, I decided to roll my own following the same idea.

The goal was to be able to take a template document containing tags and have a simple way to replace the tags with the values. A template document would look like:

Tag1 == [%tag1%]    Tag2 == [%tag2%]
The sky is [%SkyColor%].
Today is [%TodayDate%].

where the tags are delimited by the [%...%] markers. If we set the tags to the following values:

[%tag1%] = This is Tag 1
[%tag2%] = This is not Tag 1 but Tag 2
[%SkyColor%] = blue
[%TodayDate%] = 11/19/2004

then the resulting output of the template parser should be:

Tag1 == This is Tag 1    Tag2 == This is not Tag 1 but Tag 2
The sky is blue.
Today is 11/19/2004.

The Code

The TemplateParser class contains all the code to store the tags and process the templates. The tags are stored in a HashTable, _templateTags.

private Hashtable _templateTags = new Hashtable();

The HashTable contains a collection of TemplateTag objects. The TemplateTag object contains two string properties, Tag and Value. The Tag is used as the hashtable key and the Value is used to replace the tag in the template.

Template tags are added to the hashtable through the overloaded public method AddTag(). Individual tags can be removed with the RemoveTag() method, or all tags can be cleared with the ClearTags() method.

    public void AddTag( TemplateTag templateTag )
    {
        _templateTags[templateTag.Tag] = templateTag;
    }
    
    public void AddTag( string Tag, string Value )
    {
        AddTag( new TemplateTag( Tag, Value ) );
    }
    
    public void RemoveTag( string Tag )
    {
        _templateTags.Remove( Tag );
    }

    public void ClearTags()
    {
        _templateTags.Clear();
    }

The default tag style is [%...%] and the tags are found by using the Regular Expression @"(\[%\w+%\])". This can be changed by changing the MatchPattern property of the TemplateParser.

The Regex.Replace function has an overloaded operator that accepts a delegate function. The delegate function needs to be passed a single System.Text.RegularExpressions.Match parameter and returns a string.

The delegate function for the NetTemplate checks to see if the Match.Value, which is the template tag, is in the tag HashTable. If the tag exists, the tag's Value from the HashTable is returned, else an empty string is returned.

    private string _replaceTagHandler( Match token )
    {
        if ( _templateTags.Contains( token.Value ) )
            return ((TemplateTag) _templateTags[token.Value]).Value;
        else
            return string.Empty;
    }

The public ParseTemplateString() method takes the string to be parsed and returns the parsed string. This allows you to process single strings and not just limit you to processing template files.

    public string ParseTemplateString( string Template )
    {
        MatchEvaluator replaceCallback = new MatchEvaluator( _replaceTagHandler );
        string newString = Regex.Replace( Template, _matchPattern, replaceCallback );

        return newString;
    }

The ParseTemplateFile() takes the filename of a template, reads the contents into a file buffer, then passes the the buffer to the ParseTemplateString().

    public string ParseTemplateFile( string TemplateFilename )
    {
        string fileBuffer = _fileToBuffer( TemplateFilename );
        return ParseTemplateString( fileBuffer ); 
    } 
    
    private string _fileToBuffer( string Filename ) 
    {
        if( !File.Exists( Filename ) ) 
            throw new ArgumentNullException( Filename, 
                  "Template file does not exist" ); 
            
        StreamReader reader = new StreamReader( Filename );
        string fileBuffer = reader.ReadToEnd();
        reader.Close();

        return fileBuffer;
    }

Using the TemplateParser

To parse a template string, create an instance of the TemplateParser, add the tags and values, then execute the method ParseTemplateString():

   public void SimpleParse() 
    {

        TemplateParser tp = new TemplateParser();
        tp.AddTag( new TemplateTag( "[%Title%]", "Template Test" ) );
        
        string inputString = @"This is a [%Title%] or is it.";
        
        string outputString = tp.ParseTemplateString(inputString);

        string expectedResults = @"This is a Template Test or is it.";
        
        Assert.AreEqual( expectedResults, outputString);
    }

The only difference needed to parse a template file is to pass in the name of the file to the ParseTemplateFile() method:

    public void TemplateFileTester()
    {
        string TemplateFilename = @".\TemplateTest.txt";
        
        TemplateParser tp = new TemplateParser();
        tp.AddTag( new TemplateTag( "[%test1%]", "test1 SAT") );
        tp.AddTag( new TemplateTag( "[%test2%]", "test2 SAT") );
        tp.AddTag( new TemplateTag( "[%test3%]", "test3 SAT") );
        tp.AddTag( new TemplateTag( "[%test4%]", "test4 SAT") );
        tp.AddTag( new TemplateTag( "[%test5%]", "test5 SAT") );
        tp.AddTag( new TemplateTag( "[%test6%]", "test6 SAT") );

        string parsedFile = tp.ParseTemplateFile(TemplateFilename);

        string TemplateOutput = @".\TemplateTestOut.txt";
        if (File.Exists(TemplateOutput)) File.Delete(TemplateOutput);
        StreamWriter writer = new StreamWriter(TemplateOutput);
        writer.Write(parsedFile);
        writer.Flush();
        writer.Close();
    }

Points of Interest

I was really surprised at how simple the templating system turned out to build. Granted I have not performed any measurements to see how fast it will run, but given that most of my template files are going to be small, this should not be a big issue.

A future modification I would like to make is using an interface for the TemplateTag object. This would allow me to use more than just string values for the template.

History

  • December 9, 2004: Article submission.
  • December 10, 2004: Corrections and changes per SimmoTech.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here