Click here to Skip to main content
Click here to Skip to main content
Go to top

Ader Template Engine

, 30 Aug 2004
Rate this:
Please Sign up or sign in to vote.
Library for generating text output from source template and input parameters.

Introduction

Ader TemplateEngine is a .NET class library (written in C#) for generating text output from source template and input parameters. It can be used in many scenarios: website page building, email generation, XML generation, source code generation, etc. Its idea is based on Antlr stringTemplate, but the syntax is based on ColdFusion language.

Currently, .NET Ader TemplateEngine works with .NET 2.0 only, but I have plans on making it work with .NET 1.0/1.1 and maybe Java. Ader TemplateEngine is released under GNU General Public License.

Here is a very simple template:

Thank You for your order #order.billFirstName# #order.billLastName#.
<br>
Your Order Total is: #format(order.total, "C")#

The templates can have expressions, if/elseif/else statements, foreach statement and other templates.

Templates API:

There are two classes mainly used in Template Engine: Template and TemplateManager. Template holds a single instance of a template and TemplateManager is used for executing templates. Easiest way of creating templates is by using static methods of Template or TemplateManager:

Template template = Template.FromString(string name, string data);
Template template = Template.FromFile(string name, string filename);

Then you use it to instantiate TemplateManager:

TemplateManager mngr = new TemplateManager(template);

or even easier:

TemplateManager mngr = TemplateManager.FromFile(filename);
TemplateManager mngr = TemplateManager.FromString(template);

When using FromString method, the string passed contains template code. This method can be used to dynamically generate text without having templates in files. You use SetValue(string name, object value); to add values that can be used within the templates.

E.g.:

mngr.SetValue("customer", new Customer("Tom", "Jackson"));

Then you can refer to customer within the template. You can use any type of object for value. When the value of variable is to be output, ToString() method will be called.

Expressions

Expressions are enclosed with # (hash or pound) characters:

Example:

#firstName#

This example will output value of first name. If you need to output # character, just escape it with another #.

Example:

Your SS## is #ssnumber#

Inside of expression block, you can output any variable:

#somevar#

Access property of a variable:

#somestring.Length#

Property name is not case sensitive. So you can call: #string.length# or #string.LENGTH#. Or call a function:

#trim(somename)#

There are several built in functions, and additional functions can be easily added. The built in functions are:

  • equals(obj1, obj2) - invokes equals method on obj1 with obj2 as parameter. Returns boolean value.
  • notequals(obj1, obj2) - Returns !equals(obj1, obj2). Is equivalent to calling: not(equals(obj1, obj2)).
  • iseven(num) - tests whether number is an even number.
  • isodd(num) - tests whether number is an odd number.
  • isempty(string) - tests whether string has 0 characters. Same as equals(string.Length, 0).
  • isnotempty(string) - tests whether string has at least 1 character.
  • isnumber(num) - tests whether num is of numeric type.
  • toupper(string) - converts string to upper case.
  • tolower(string) - converts string to lower case.
  • isdefined(varname) - tests whether variable named varname is defined.
  • ifdefined(varname, value) - returns value if varname is defined. Especially useful: #ifdefined("name", name)# - will output value of name if it's defined, otherwise will output nothing.
  • len(string) - returns length of string.
  • tolist(collection, property, delim) - will convert collection to string with delim as separator. If you pass property, the value of the property will be evaluated on each element of the collection. If you omit property, then the object itself will be used.

    Example: suppose you have list as:

        ArrayList list = new ArrayList();
        list.Add("one");
        list.Add("two");
        list.Add("three");
        template.SetValue("mylist", list);

    Then in your template:

    #toList(mylist, " & ")#

    the output will be: one & two & three

    Suppose you have list as:

        list.Add(new Customer("Tom", "Whatever"));
        list.Add(new Customer("Henry", "III"));
        list.Add(new Customer("Tom", "Jackson"));
        template.SetValue("mylist", list);

    Then in template:

    #toList(mylist, "firstName", ",")#

    The output will be: Tom,Henry,Tom.

  • isnull(obj) - tests whether obj is null.
  • not(boolvalue) - returns not (!) of boolean value.
  • iif(booleanExpression, iftruevalue, iffalsevalue) - same as booleanExpression ? iftruevalue : iffalsevalue in C#.

    Example:

    #iif(isodd(i), "bgcolor=yellow", "bgcolor=red")#

    will output bgcolor=yellow if i is odd number, and bgcolor=red if i is not odd number.

  • format(object, formatstring) - will call ToString(formatstring) on object. object has to implement IFormattable interface, otherwise ToString() will be called.

    Example: suppose total is decimal with value 1208.45:

    #format(total, "C")#

    will output: $1,208.45

  • trim(string) - will trim string object
  • filter(collection, booleanproperty) - will return new List from collection for those objects whose boolean property property evaluates to true.
  • gt(obj1, obj2) - will return true if obj1 > obj2. (obj1 and obj2 must implement IComparable. All numeric types do).
  • lt(obj1, obj2) - will return true if obj1 < obj2. (obj1 and obj2 must implement IComparable. All numeric types do).
  • compare(obj1, obj2) - will return -1 if obj1 < obj2, 0 if obj1 = obj2, and 1 if obj1 > obj2. (obj1 and obj2 must implement IComparable. All numeric types do).
  • or(bool1, bool2) - will return true if either bool1 or bool2 are true.

    Example:

    #or(equals(state, "IL"), equals(state, "NY"))# - 
    returns true if state is either IL or NY
  • and(bool1, bool2) - will return true if both bool1 and bool2 are true.
  • comparenocase(string1, string2) - will do case insensitive comparison of string1 and string2 and return true if they are equal.
  • stripnewlines(string) - will return all \r\n instances and replace them with space.

Built In Tags:

  • IF

    You can also conditionally output text based on some expression using special if tag:

    <ad:if test="#booleanexpression#">
    <ad:elseif test="#bool#">
    <ad:else>
    </ad:if>

    elseif and else are optional. If test of "if" evaluates to true, then block inside of "if" will be output, otherwise elseif will be tested (if exists) and then else.

    Example:

    <ad:if test="#equals(cust.country, "US"))#">
    You are US customer.
    <ad:else>
    You are from: #cust.country# country.
    </ad:if>

    If cust.country is "US" then the output will be: You are US customer.

  • FOREACH

    You can loop through collection of elements (any object that implements IEnumerable interface) using FOREACH tag.

    <ad:foreach collection="#collection#" var="cust" index="i">
    #i#: #cust.lastname#, #cust.firstname#
    </ad:foreach>

    Suppose customers is array of customer objects:

    customers = Customer("Tom", "Jackson")
    Customer("Mary", "Foo")

    The output will be:

    1. Jackson, Tom
    2. Foo, Mary

    During execution, variable name that is passed as var attribute will be assigned with element from the collection. index attribute can be omitted, and is used to represent index variable for the loop. It starts with 1 and gets incremented with each iteration.

Custom Templates:

You can also create your own templates inside of template file, that you can call. You do that using template tag:

<ad:template name="ShowCustomer">
#customer.lastname#, #customer.firstname# 
</ad:template>

<ad:showcustomer customer="#cust#" />

You can pass any attribute to the template, and you can use those inside of the template. The template can also access all variables that are defined outside of the template. When calling template, you have to put trailing slash at the end, or put closing tag:

<ad:showcustomer />

or

<ad:showcustomer></ad:showcustomer>

The template also receives special variable: innerText that is the content of executing the inner elements of calling template.

<ad:template name="bold">
<b>#innerText#</b>
</ad:template>

<ad:bold>#cust.lastname#, #cust.firstname#</ad:bold>

the output will be:

<b>Jackson, Tom</b>
(if customer is Tom Jackson)

You can also nest those:

<ad:template name="italic">#innerText#</ad:template>
<ad:bold><ad:italic>This will be bold and italic</ad:italic></ad:bold>

You can also invoke templates based on the name, using apply tag:

<ad:apply template="#usetemplate#">this is content</ad:apply>

If usetemplate is "bold", then "bold" template will be called.

Templates can be nested inside other templates:

<ad:template name="doit">
    <ad:template name="colorme">
      <font color=#color#>#innerText#</font>
    </ad:template>

    <ad:colorme color="blue">colorize me</ad:colorme>
</ad:template>

colorme template can only be used within doit template. Templates can also be added programmatically:

TemplateManager mngr = ...;
mngr.AddTemplate(Template.FromString("bold", "<b>#innerText#</b>"));

Now, bold template can be used anywhere within processing. Here is a sample based on order confirmation:

    class Order
    {
        string firstname, lastname, address1, city, state, zip, country;

        public string Address1
        {
            get { return this.address1; }
        }

        public string City
        {
            get { return this.city; }
        }

        public string Country
        {
            get { return this.country; }
        }

        public string Firstname
        {
            get { return this.firstname; }
        }

        public string Lastname
        {
            get { return this.lastname; }
        }

        public string State
        {
            get { return this.state; }
        }

        public string Zip
        {
            get { return this.zip; }
        }
    }
    
    Order order = GetOrder();
    TemplateManager mngr = TemplateManager.FromFile("order-confirmation.st");
    mngr.SetValue("order", order);
    System.IO.StringWriter writer = new System.IO.StringWriter();
    mngr.Process(writer);
    
    string emailBody = writer.ToString();

order-confirmation.st

<ad:showitem>
#item.sku# - #item.name#<br>
<ad:if test="#equals(item.qty, 1)#">
Price: #format(item.price, "C")#<br>
<ad:else>
You bought #item.qty# items for #format(item.price, "C")# 
                        (total: #format(item.total, "C")#)
</ad:if>
</ad:showitem>

#order.firstname# #order.lastname#<br>
#order.address1#<br>
<ad:if test="#isnotempty(order.address2)#">#order.address2#<br></ad:if>
#order.city#, #order.zip# #order.state#
<br>
<table>
<ad:foreach collection="#order.orderitems#" var="orderitem" index="i">
<tr>
    <td>#i#.</td>
    <td bgcolor="#iif(isodd(i), "##DEDEDE", "white")#">
    <ad:showitem item="#orderitem#" />
    </td>
</tr>
</ad:foreach>
</table>
Shipping: #format(order.shipping, "C")#<br>
Taxes: #format(order.tax, "C")#<br>
Order Total: #format(order.total, "C")#<br>

Description of order-confirmation.st:

First, showitem template is defined which shows a single line item of the order. item is passed as attribute to showitem. Then address is shown. Note how if is used to conditionally display second line of address with ending <br> tag. Then each line item of order is looped through using ad:foreach tag. iif function is used to color every other line with #DEDEDE color.

Example #2 for constructing complex SQL queries:

string[] cols = new string[]{"id", "name", "email"};
TemplateManager mngr = TemplateManager.FromFile(file);
mngr.SetValue("colums", cols);
mngr.SetValue("tablename", "customer");
string query = mngr.Process();

And the template file is:

select #toList(columns, ",")# from #tablename#

Example 1 project has two sample templates that are used to process the same data. First, it outputs it as a C# class to the screen, then it uses HTML template to create HTML file.

If you have any questions, you can use AderTemplates forums.

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

Share

About the Author

Werdna
Chief Technology Officer
United States United States
No Biography provided

Comments and Discussions

 
Questionproblem with adder template PinmemberTridip Bhattacharjee5-Dec-12 1:18 
AnswerRe: problem with adder template PinmemberWerdna5-Dec-12 5:02 
GeneralRe: problem with adder template PinmemberTridip Bhattacharjee5-Dec-12 19:30 
GeneralRe: problem with adder template PinmemberWerdna7-Dec-12 3:36 
QuestionDoes it work with RTF PinmemberBalck.01-Feb-11 20:14 
GeneralMy 5 Pinmemberrobocodeboy16-Dec-10 22:29 
GeneralMy vote of 5 Pinmemberrobocodeboy16-Dec-10 21:59 
Generalmake it support external template files [modified] PinmemberUnruled Boy20-Sep-07 23:46 
Generalmath expression evaluation PinmemberUnruled Boy11-Aug-05 22:23 
GeneralRe: math expression evaluation PinmemberWerdna12-Aug-05 5:20 
Questiontags executed/evaluated all at once? PinmemberUnruled Boy6-Aug-05 6:07 
AnswerRe: tags executed/evaluated all at once? PinmemberWerdna8-Aug-05 11:57 
Questionvoid -> [null] ? PinmemberUnruled Boy5-Aug-05 21:33 
AnswerRe: void -&gt; [null] ? PinmemberWerdna6-Aug-05 5:55 
Questionruntime var? PinmemberUnruled Boy5-Aug-05 18:06 
AnswerRe: runtime var? PinmemberWerdna6-Aug-05 5:53 
GeneralRe: runtime var? PinmemberUnruled Boy6-Aug-05 5:58 
Generalor() expression failed PinmemberUnruled Boy1-Aug-05 21:24 
GeneralRe: or() expression failed PinmemberWerdna2-Aug-05 3:52 
GeneralRe: or() expression failed PinmemberUnruled Boy2-Aug-05 5:05 
GeneralGPL Licence PinmemberSchnulle31-Jul-05 20:21 
GeneralRe: GPL Licence PinmemberUnruled Boy31-Jul-05 20:50 
GeneralRe: GPL Licence PinmemberWerdna1-Aug-05 3:36 
GeneralRe: GPL Licence PinmemberSchnulle5-Sep-05 2:08 
GeneralRe: GPL Licence PinmemberSteveC-A99-Feb-07 7:52 
GeneralRe: GPL Licence Pinmemberd.marzo14-Feb-06 6:47 
Questionnested function failed? PinmemberUnruled Boy31-Jul-05 19:59 
AnswerRe: nested function failed? PinmemberUnruled Boy31-Jul-05 20:02 
GeneralRe: nested function failed? PinmemberWerdna1-Aug-05 3:35 
QuestionNested templates with collections? PinmemberUnruled Boy31-Jul-05 16:58 
AnswerRe: Nested templates with collections? PinmemberWerdna31-Jul-05 17:18 
Questionwhere are the sample codes for *.sta files in &quot;Example Templates&quot;? PinmemberUnruled Boy30-Jul-05 17:04 
AnswerRe: where are the sample codes for *.sta files in &quot;Example Templates&quot;? PinmemberUnruled Boy30-Jul-05 17:19 
GeneralRe: where are the sample codes for *.sta files in "Example Templates"? PinmemberWerdna31-Jul-05 7:11 
GeneralRe: where are the sample codes for *.sta files in &quot;Example Templates&quot;? PinmemberUnruled Boy31-Jul-05 15:55 
General&lt;ad:...&gt; Tags cause blank lines PinmemberSchnulle20-Jul-05 20:52 
GeneralRe: &lt;ad:...&gt; Tags cause blank lines PinmemberWerdna27-Jul-05 14:54 
GeneralIndentation PinmemberSchnulle20-Jul-05 3:27 
GeneralRe: Indentation PinmemberWerdna20-Jul-05 4:32 
GeneralRe: Indentation PinmemberSchnulle20-Jul-05 19:09 
GeneralRe: Indentation PinmemberSchnulle5-Sep-05 2:07 
Generalsupport for all character sets PinmemberUnruled Boy7-Jun-05 22:01 
GeneralRe: support for all character sets PinmemberWerdna8-Jun-05 12:52 
Questionwhy not update the article? PinmemberUnruled Boy30-May-05 13:16 
AnswerRe: why not update the article? PinmemberWerdna7-Jun-05 9:42 
GeneralTemplateEngine Version 2 Available PinmemberWerdna18-May-05 11:46 
GeneralRe: TemplateEngine Version 2 Available PinmemberUnruled Boy19-May-05 4:21 
GeneralRe: TemplateEngine Version 2 Available PinmemberWerdna19-May-05 4:45 
GeneralRe: TemplateEngine Version 2 Available PinmemberUnruled Boy21-May-05 22:09 
GeneralRe: TemplateEngine Version 2 Available PinmemberWerdna22-May-05 5:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 31 Aug 2004
Article Copyright 2004 by Werdna
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid