Ader 模板引擎






4.92/5 (24投票s)
2004年8月31日
6分钟阅读

174747

916
用于从源模板和输入参数生成文本输出的库。
引言
Ader TemplateEngine 是一个 .NET 类库(用 C# 编写),用于从源模板和输入参数生成文本输出。它可用于多种场景:网站页面构建、电子邮件生成、XML 生成、源代码生成等。其思想基于 Antlr stringTemplate
,但语法基于 ColdFusion 语言。
目前,.NET Ader TemplateEngine 仅支持 .NET 2.0,但我计划使其支持 .NET 1.0/1.1,甚至可能是 Java。Ader TemplateEngine 在 GNU General Public License 下发布。
这是一个非常简单的模板
Thank You for your order #order.billFirstName# #order.billLastName#.
<br>
Your Order Total is: #format(order.total, "C")#
模板可以包含表达式、if
/elseif
/else
语句、foreach
语句和其他模板。
模板 API
模板引擎主要使用两个类:Template
和 TemplateManager
。Template
保存模板的单个实例,TemplateManager
用于执行模板。创建模板最简单的方法是使用 Template
或 TemplateManager
的静态方法
Template template = Template.FromString(string name, string data);
Template template = Template.FromFile(string name, string filename);
然后您可以使用它来实例化 TemplateManager
TemplateManager mngr = new TemplateManager(template);
甚至更简单
TemplateManager mngr = TemplateManager.FromFile(filename);
TemplateManager mngr = TemplateManager.FromString(template);
使用 FromString
方法时,传递的字符串包含模板代码。此方法可用于动态生成文本,而无需将模板保存在文件中。您可以使用 SetValue(string name, object value);
来添加可在模板中使用的值。
例如:
mngr.SetValue("customer", new Customer("Tom", "Jackson"));
然后您可以在模板中引用 customer
。您可以使用任何类型的对象作为值。当变量的值需要输出时,将调用 ToString()
方法。
表达式
表达式用 #
(井号)字符括起来
示例
#firstName#
此示例将输出名字的值。如果您需要输出 #
字符,只需用另一个 #
来转义它。
示例
Your SS## is #ssnumber#
在表达式块内,您可以输出任何变量
#somevar#
访问变量的属性
#somestring.Length#
属性名不区分大小写。因此,您可以调用:#string.length#
或 #string.LENGTH#
。或者调用一个函数
#trim(somename)#
有几个内置函数,并且可以轻松添加其他函数。内置函数包括
equals(obj1, obj2)
- 调用obj1
的equals
方法,并将obj2
作为参数。返回布尔值。notequals(obj1, obj2)
- 返回!equals(obj1, obj2)
。等同于调用:not(equals(obj1, obj2))
。iseven(num)
- 测试数字是否为偶数。isodd(num)
- 测试数字是否为奇数。isempty(string)
- 测试字符串是否不包含字符。与equals(string.Length, 0)
相同。isnotempty(string)
- 测试字符串是否至少包含 1 个字符。isnumber(num)
- 测试num
是否为数字类型。toupper(string)
- 将string
转换为大写。tolower(string)
- 将string
转换为小写。isdefined(varname)
- 测试名为varname
的变量是否已定义。ifdefined(varname, value)
- 如果varname
已定义,则返回该值。特别有用:#ifdefined("name", name)#
- 如果name
已定义,则输出其值,否则什么也不输出。len(string)
- 返回string
的长度。tolist(collection, property, delim)
- 将collection
转换为字符串,以delim
作为分隔符。如果传递了property
,则将在集合的每个元素上评估property
的值。如果省略property
,则使用对象本身。示例:假设您有一个列表
ArrayList list = new ArrayList(); list.Add("one"); list.Add("two"); list.Add("three"); template.SetValue("mylist", list);
然后在您的模板中
#toList(mylist, " & ")#
输出将是:one & two & three
假设您有一个列表
list.Add(new Customer("Tom", "Whatever")); list.Add(new Customer("Henry", "III")); list.Add(new Customer("Tom", "Jackson")); template.SetValue("mylist", list);
然后在模板中
#toList(mylist, "firstName", ",")#
输出将是:Tom,Henry,Tom。
isnull(obj)
- 测试obj
是否为null
。not(boolvalue)
- 返回布尔值的非 (!)。iif(booleanExpression, iftruevalue, iffalsevalue)
- 相当于 C# 中的booleanExpression ? iftruevalue : iffalsevalue
。示例
#iif(isodd(i), "bgcolor=yellow", "bgcolor=red")#
如果
i
是奇数,则输出 bgcolor=yellow,如果i
不是奇数,则输出 bgcolor=red。format(object, formatstring)
- 将在object
上调用ToString(formatstring)
。object
必须实现IFormattable
接口,否则将调用ToString()
。示例:假设
total
是值为 1208.45 的 decimal#format(total, "C")#
将输出:$1,208.45
trim(string)
- 将修剪string
对象filter(collection, booleanproperty)
- 将从collection
中返回一个新的 List,包含那些布尔属性 property 评估为true
的对象。gt(obj1, obj2)
- 如果obj1
>obj2
,则返回true
。(obj1
和obj2
必须实现IComparable
。所有数字类型都实现了)。lt(obj1, obj2)
- 如果obj1
<obj2
,则返回true
。(obj1
和obj2
必须实现IComparable
。所有数字类型都实现了)。compare(obj1, obj2)
- 如果obj1
<obj2
,则返回 -1;如果obj1
=obj2
,则返回 0;如果obj1
>obj2
,则返回 1。(obj1
和obj2
必须实现IComparable
。所有数字类型都实现了)。or(bool1, bool2)
- 如果bool1
或bool2
为true
,则返回true
。示例
#or(equals(state, "IL"), equals(state, "NY"))# - returns true if state is either IL or NY
and(bool1, bool2)
- 如果bool1
和bool2
都为true
,则返回true
。comparenocase(string1, string2)
- 将执行不区分大小写的比较string1
和string2
,如果它们相等,则返回true
。stripnewlines(string)
- 将返回所有\r\n
实例并将其替换为空格。
内置标签
IF
您还可以根据某些表达式有条件地输出文本,使用特殊的
if
标签<ad:if test="#booleanexpression#"> <ad:elseif test="#bool#"> <ad:else> </ad:if>
elseif
和else
是可选的。如果 "if
" 的测试结果为true
,则输出 "if
" 内块的内容,否则测试elseif
(如果存在),然后测试else
。示例
<ad:if test="#equals(cust.country, "US"))#"> You are US customer. <ad:else> You are from: #cust.country# country. </ad:if>
如果
cust.country
是 "US",则输出将是:You are US customer.FOREACH
您可以使用
FOREACH
标签循环遍历元素集合(任何实现IEnumerable
接口的对象)。<ad:foreach collection="#collection#" var="cust" index="i"> #i#: #cust.lastname#, #cust.firstname# </ad:foreach>
假设
customers
是客户对象数组customers = Customer("Tom", "Jackson") Customer("Mary", "Foo")
输出将是:
- Jackson, Tom
- Foo, Mary
在执行过程中,作为
var
属性传递的变量名将被赋为集合中的元素。index
属性可以省略,用于表示循环的索引变量。它从 1 开始,并在每次迭代中递增。
自定义模板
您还可以在模板文件中创建自己的模板,然后调用它们。您通过 template
标签来做到这一点
<ad:template name="ShowCustomer">
#customer.lastname#, #customer.firstname#
</ad:template>
<ad:showcustomer customer="#cust#" />
您可以将任何属性传递给模板,并且您可以在模板内部使用这些属性。模板还可以访问在模板外部定义的所有变量。调用模板时,必须在末尾加上斜杠,或者使用闭合标签
<ad:showcustomer />
或
<ad:showcustomer></ad:showcustomer>
模板还接收一个特殊变量:innerText
,它是调用模板的内部元素执行的内容。
<ad:template name="bold">
<b>#innerText#</b>
</ad:template>
<ad:bold>#cust.lastname#, #cust.firstname#</ad:bold>
输出将是
<b>Jackson, Tom</b>
(if customer is Tom Jackson)
您也可以嵌套它们
<ad:template name="italic">#innerText#</ad:template>
<ad:bold><ad:italic>This will be bold and italic</ad:italic></ad:bold>
您还可以使用 apply
标签根据名称调用模板
<ad:apply template="#usetemplate#">this is content</ad:apply>
如果 usetemplate
是 "bold",则将调用 "bold" 模板。
模板可以嵌套在其他模板中
<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
模板只能在 doit
模板内部使用。模板也可以通过编程方式添加
TemplateManager mngr = ...;
mngr.AddTemplate(Template.FromString("bold", "<b>#innerText#</b>"));
现在,bold
模板可以在处理的任何地方使用。这是一个基于订单确认的示例
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>
order-confirmation.st 的描述
首先,定义了 showitem
模板,它显示订单的单个行项。item
作为属性传递给 showitem
。然后显示地址。注意如何使用 if
来有条件地显示地址的第二行并加上 <br> 标签。然后使用 ad:foreach
标签循环遍历订单的每一行项。使用 iif
函数为每隔一行着色为 #DEDEDE。
示例 #2:构建复杂的 SQL 查询
string[] cols = new string[]{"id", "name", "email"};
TemplateManager mngr = TemplateManager.FromFile(file);
mngr.SetValue("colums", cols);
mngr.SetValue("tablename", "customer");
string query = mngr.Process();
模板文件是
select #toList(columns, ",")# from #tablename#
示例 1 项目有两个示例模板,用于处理相同的数据。首先,它将数据作为 C# 类输出到屏幕,然后使用 HTML 模板创建 HTML 文件。
如果您有任何问题,可以使用 AderTemplates 论坛。