模板引擎StringTemplate
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
模板引擎StringTemplate
1,特点
1,强制Model和View的分离,也就是View负责呈现,不能修改Model。
同时View中不⽤来包括任何业务逻辑,详见作者的论⽂(,)。
2,Lazy-Evaluation,所有的Attribute直到st.ToString()调⽤时才evaluate.这样的好处是setAttribute是Order Independent。
3,Recursive enable。
2,属性(attribute)
StringTemplate语法有两种组成元素,⼀种是属性(attribute),另⼀种是普通字符(plain text)。
在$…$中包围的是属性,其余的都是普通字符。
⽐如:
select $column$ from $table$
其中红⾊部分的$column$和$table$都是属性。
在模板引擎呈现,即调⽤st.ToString()时,属性被实际值替换,⽽普通字符原封不动地输出。
我们可以通过下⾯的⽅式来向模板“填充”(push)属性:
StringTemplate st = new StringTemplate("select $column$ from $table")
st.SetAttribute("column", "uid");
st.SetAttribute("table", "users");
Console.WriteLine(st.ToString()); //output: select uid from users
属性的填充⽅式很简单,就是调⽤attribute.ToString()⽅法的结果,如果attribute是null不会抛出异常,⽽是输出空字符串。
在.NET实现中attribute可以引⽤.net对象字段或属性(不区分⼤⼩写),如果字段和属性同名则属性优先。
attribute也可以是Dictionary或HashTable的Key,这样会找到该Key对应的值,然后在调⽤这个值的ToString()⽅法。
⽰例:
class Query
{
public string column = "a";
public string table = "t";
public string Table { get { return "T"; } }
}
StringTemplate st = new StringTemplate("select $q.column$ from $q.TABLE$");
Query q = new Query();
st.SetAttribute("q",q);
Console.WriteLine(st.ToString()); //output: select a from T
2.1,间接属性(indirect property names)
你可以通过下⾯的attribute.(anotherAttr)语法来引⽤间接属性:
StringTemplate st1 = new StringTemplate("$q.(property)$"); //property是间接属性,也是⼀个attribute
Query q = new Query();
st1.SetAttribute("q", q);
st1.SetAttribute("property", "table");
Console.WriteLine(st1.ToString()); //output: t
有些StringTemplate的保留字(reserved keyword),是不能作为属性的:
如果attribute中的property和这些保留字重复了,StringTemplate就会报错,解决⽅案是使⽤间接属性:
class Query
{
public string first = "NumberOne";
}
StringTemplate st = new StringTemplate("select $q.(IndirectAttr)$");
Query q = new Query();
st.SetAttribute("q", q);
st.SetAttribute("IndirectAttr", "first");
Console.WriteLine(st.ToString()); //select NumberOne
//或者直接⽤Literal String: select $q.("first")$
2.2,多值属性(multi-valued attribute)
所谓多值属性其实就是像数组⼀样的可迭代对象。
多值属性通常有两种输出⽅式:1,类似于String.Join()的拼接。
2,迭代。
A)拼接
StringTemplate st = new StringTemplate("select $columns$ from users");
List<string> columns = new List<string>();
columns.Add("a");
columns.Add("b");
st.SetAttribute("columns", columns);
Console.WriteLine(st.ToString()); //output: select ab from users
StringTemplate st1 = new StringTemplate("select $columns; separator=\",\"$ from users");
st1.SetAttribute("columns", "a");
st1.SetAttribute("columns", "b"); //此时columns变成multi-valued,作者讲其实SetAttribute应该叫做AddAtribute才对,对于同名attribute他不是替换⽽是添加Console.WriteLine(st1.ToString()); //output: select a,b from users
StringTemplate st2 = new StringTemplate("select $columns; separator=dilimiter$ from users");
st2.SetAttribute("columns", "a");
st2.SetAttribute("columns", "b");
st2.SetAttribute("dilimiter", ","); //separator也可以使expr
Console.WriteLine(st2.ToString()); //output: select a,b from users
B)迭代
StringTemplate st = new StringTemplate("select $columns:{<i>$it$</i>}$ from users"); //迭代{...}中的anonymous template,并以⾃动填充it为当前元素
st.SetAttribute("columns", "a");
st.SetAttribute("columns", "b");
Console.WriteLine(st.ToString()); //output: select <i>a</i> <i>b</i> from users
StringTemplate st1 = new StringTemplate("select $columns:{col|<i>$col$</i>}$ from users"); //⾃动填充col为当前元素,同时it还是要填充的
st1.SetAttribute("columns", "a");
st1.SetAttribute("columns", "b");
Console.WriteLine(st1.ToString()); //output: select <i>a</i> <i>b</i> from users
C)函数
StringTemplate⾥⾯内置了⼀些函数⽤来操作multi-valued attributes,把他当做Lisp中的List⼀样操作,通过⼀些组合能实现⽜逼功能。
StringTemplate st = new StringTemplate("[$first(columns)$] [$last(columns)$] [$first(rest(columns))$]");
st.SetAttribute("columns", "a");
st.SetAttribute("columns", "b");
st.SetAttribute("columns", "c");
Console.WriteLine(st.ToString()); //output: [a] [c] [b]
2.3 属性的呈现(attribute render)
我们知道属性最后在模板中的呈现都是调⽤属性的ToString()⽅法,但是我们有时候想在模板呈现的时候改变某个属性的呈现,那么我们该怎么做呢?试想⼀下,我们对于DateTime,我们只想显⽰⽇期,⽽不想显⽰具体的时间,那我们怎么处理呢?⼀个办法是新建⼀个类型,然后重写ToString⽅法,但这样改动有点⼤,因为你需要在Model中所有⽤到这个DateTime的地⽅都要做类型转换。
StringTemplate提供的⼀个接⼝IAttributeRenderer⽤来做这种⼯作,该接⼝只负责⼀个类型的呈现,我们可以向⼀个模板注册这个呈现⽅式:
class DateTimeRender:IAttributeRenderer
{
public string ToString(object o)
{
DateTime dt = (DateTime)o;
return dt.ToString("yyyy.MM.dd");
}
}
StringTemplate st = new StringTemplate("$d$");
st.SetAttribute("d", DateTime.Now);
Console.WriteLine(st.ToString());
st.RegisterAttributeRenderer(typeof(DateTime), new DateTimeRender()); //register the attribute render for DateTime
Console.WriteLine(st.ToString());
3,StringTemplateGroup
3.1 从⽂件中载⼊模板
模板时常是写在⽂件中的,⽐如⼀些作为html页⾯的模板。
如果想从⽂件中装载模板我们可以使⽤StringTemplateGroup:
string rootDirectoryOfTemplate = bine(AppDomain.CurrentDomain.BaseDirectory,"Query");
StringTemplateGroup group = new StringTemplateGroup("queryTemplate",rootDirectoryOfTemplate);
StringTemplate st1 = group.GetInstanceOf("query"); //load ROOT_PATH/query.st
st1.SetAttribute("func","sysdate()");
Console.WriteLine(st1.ToString());
StringTemplate st2 = group.GetInstanceOf("\\sub\\query"); //load ROOT_PATH/sub/query.st
st2.SetAttribute("func", "sysdate()");
Console.WriteLine(st2.ToString());
3.2 命名模板(Group Files)
如果想模块化模板,⼀个⽅式是使⽤单独的⽂件。
但是如果模板很短⼩这样会产⽣很多⽂件,StringTemplate提供了Group Files⽤来定义短⼩的模板模块,你可以通过给通过名称来引⽤定义好的模板模块。
命名模板的定义规则是:
templateName(arg1, arg2, ..., argN) ::= "single-line template"
or
templateName(arg1, arg2, ..., argN) ::= <<
multi-line template
>>
or
templateName(arg1, arg2, ..., argN) ::= <%
multi-line template that ignores indentation and newlines
%>
命名模板还提供了模板参数,你可以通过SetAttribute来设置模板的参数。
⽰例:
string groupDef = @"
group mygroup;
vardef(type,name) ::= ""<type> <name>;""
method(type,name,args) ::= <<
<type> <name>(<args; separator="","">) {
<statements; separator=""\n"">
}
>>
";
StringTemplateGroup stg = new StringTemplateGroup(new StringReader(groupDef));
var vardef = stg.GetInstanceOf("vardef");
vardef.SetAttribute("type", "int");
vardef.SetAttribute("name", "sum");
Console.WriteLine(vardef.ToString()); //output: int sum;
4,⼦模板(subtemplate)
4.1 匿名⼦模板(anonymous subtemplate)
我们可以对属性或多值属性应⽤匿名⼦模板,语法我们在多值属性的迭代中已经见识了:$attr:{ <anonymous template> }$。
匿名模板还可以嵌套:
StringTemplate st = new StringTemplate("$x:{<b>$it$</b>}:{<li>$it$</li>}; separator=\"\n\"$");
st.SetAttribute("x", 1);
st.SetAttribute("x", 2);
st.SetAttribute("x", 3);
Console.WriteLine(st.ToString());
这样我们就可以对每个元素先应⽤<b>,然后在应⽤<li>。
对于多值属性,我们还可以对每个属性交替地应⽤不同的⼦模板,语法跟上⾯的就跟上⾯应⽤多个匿名模板很类似,将“:”变成“,”:
StringTemplate st = new StringTemplate("$x:{<red>$it$</red>},{<blue>$it$</blue>}; separator=\"\n\"$");
st.SetAttribute("x", 1);
st.SetAttribute("x", "jatsz");
st.SetAttribute("x", 2);
st.SetAttribute("x","git");
Console.WriteLine(st.ToString());
4.2 条件包含⼦模板
$if(x)$
...
$elseif(y)$
...
$elseif(z)$
...
$else$
...
$endif$
除了false, null, empty(list,hastable)视作false以外,其他的值都被视作true。
我们可以通过条件表达式来包含(include)不同的⼦模板:我们共有三个模板⽂件,分别位于Views⽬录下的index.st, sub/login.st, sub/logout.st,⽂件的内容如下:
===index.st
$if(loggedin)$
$sub/logout(lnk=linkUrl)$
$else$
$sub/login(lnk=linkUrl)$
$endif$
===sub/login.st
<a href="$lnk$">Please click to login</a>
==sub/logout.st
<a href="$lnk$">Please click to logout</a>
我们的程序代码如下:
StringTemplateGroup stg = new StringTemplateGroup("index", AppDomain.CurrentDomain.BaseDirectory + "views");
var stIndex = stg.GetInstanceOf("index");
stIndex.SetAttribute("loggedin", true);
stIndex.SetAttribute("linkUrl","/logout");
Console.WriteLine(stIndex.ToString());
stIndex.Reset(); //or stIndex.RemoveAttribute("loggedin");
stIndex.SetAttribute("loggedin", false);
stIndex.SetAttribute("linkUrl", "/login");
Console.WriteLine(stIndex.ToString());
5,杂项
5.1 未涉及的部分
Group Interface
Template Inheritance
5.2 链接
本⽂⽰例使⽤的是StringTemplate v3版。