没有用NVelocity时,我们在后台什么一个xml格式的数据通常是这样的:
StringBuilder usersXML = new StringBuilder(); // 获取用户信息列表,烦人的sql语句了不见了,coding时是不是很舒服呢 List<UserInfo> users = userInfoDAO.SelectAll(); usersXML.Append("<users count=\""+ users.Count +"\" title=\"使用字符串拼接的例子\">"); foreach (UserInfo user in users) { usersXML.Append("<user>") .Append("<name>").Append(user.Name).Append("</name>") .Append("<sex>").Append(user.Sex).Append("</sex>") .Append("<city>").Append(user.City).Append("</city>") .Append("</user>"); } usersXML.Append("</users>"); Response.Clear(); Response.ContentType = "text/xml"; Response.Write("<?xml version=\"1.0\" encoding=\"utf-8\" ?>"); Response.Write(usersXML.ToString()); Response.End();
这种方式实现挺简单的,看起来效果也不错,于是心底暗自偷笑:“真是小菜一碟,数据准备好了,接下来可以玩Ajax了,多么酷的效果哦,爽…….”。可别得意,先来看这么做存在的一个问题,一个很严重的问题:
xml格式被写死在代码里面了。想一下,如果以后我想再为user添加联系方式,爱好等时,我们得从一大堆乱七八糟的代码中找出这段生成xml格式数据的代码,然后再接着Append("<connectType>").Append(user.ConnectType).Append("</connectType>"),如此之类…..
一阵编码之后,搞定了。长吁之后,接着编译代码,打包部署…….
刚准备休息,boss跑过来:“你把这个user改成userInfo吧,人家需要userInfo开始的数据……”。
”DM,那家伙是不是白痴呀,怎么老叫改这改那的…….”。不由一身冷汗,还好,没说出口,只是心底暗骂而已,要不肯定被炒鱿鱼了哦。
“一分钟可以搞定吗?改几个字符串而已……”。
“哪有那么快,我还得编译代码呢!“。一副不服气的样子,欠扁……
……
其实刚才都是我遐想的,其实问题没那么严重,只是改些字符串而已。但老改确实挺烦人的,得重新编译代码倒是事实。而且,这些确实只是显示格式而已,把这个仅用来定义显示样式的格式写死在代码里面确实挺让人郁闷的。
要是直接写个xml,像这个样子:
<?xml version="1.0" encoding="utf-8" ?> <users count="用户人数" title="标题"> <!-- 要是可以在这边来个循环,把用户循环显示出来就好了--> <user> <name>用户名</name> <sex>性别</sex> <city>城市</city> </user> </users> <?xml version="1.0" encoding="utf-8" ?> <users count="用户人数" title="标题"> <!-- 要是可以在这边来个循环,把用户循环显示出来就好了--> <user> <name>用户名</name> <sex>性别</sex> <city>城市</city> </user> </users>
到时候我们给这个xml一个人员列表的数据,让它可以生成一个人员列表的xml就好了,哎,一副无助的样子……
可事实上真的有这样的工具,我们管它叫模板引擎,NVelocity就属于这类。只要使用模板语言(template language)来编写好模板,到时候给模板引擎解析,并生成我们需要的数据。
好,既然这样,下面我们来看一下生成貌似上面xml格式数据的模板应该怎么写:
<?xml version="1.0" encoding="utf-8" ?> #foreach($u in $ListUsers) #beforeall <users count="$Count" title="$Title"> #each <user> <name>$u.Name</name> <sex>$u.Sex</sex> <city>$u.City</city> </user> #afterall </users> #nodata <users>暂无用户资料</users> #end
我们只需要给这个模板一个ListUsers的人员列表数据,模板引擎就会帮我们生成需要的数据,嘿嘿,是不是很爽呀…….
“可是我不会模板语言呀…….“。
“BC,不会不知道学呀,真是的……“,此人一副傲慢的样子。
鄙视那人,“学就学,谁怕谁呀,咱们又不比你笨……“,于是装着一副可以战胜一切困难的样子,其实心里一点底气都没有。
其实,学过一些编程语言后,在读模板语言写的代码时,基本上都能够猜出是什么意思。
我们现在来看看上面那个模板是什么意思:
#foreach($u in $ListUsers)其实跟C#里面的foreach(Java中的扩展for)几乎一样,就遍历ListUsers里面的数据,每次遍历的值放到对象u中,接下来
#beforeall:就是在遍历ListUsers前,先输出<users count="$Count" title="$Title">
#each:就是每遍历一下,得到一个u,就输出
<user>
<name>$u.Name</name> //$u.Name就是输出u对象的Name属性,在Java中就肯定是调用getName()方法了哦,C#实现的NVelocity也延续了java的习惯,就是说,这个也可以表示调用getName()方法,具体是直接调用属性,还是调用方法得看具体情况,NVelocity会自动辨别的
<sex>$u.Sex</sex>
<city>$u.City</city>
</user>
#afterall:遍历完ListUsers后,输出</users>
#nodata:如果没数据的话,直接数据<users>暂无用户资料</users>
#end:表示遍历完毕的结束标志
看看,是不是真的很简单呢!
好了,现在我们来看一下在服务器端怎么调用这个模板,并传给这个模板ListUsers这个参数:
// 获取到用户列表 IList<UserInfo> listUsers = UserInfoService.GetAllUsers(); // 创建一个模板引擎 VelocityEngine vltEngine = new VelocityEngine(); // 文件型模板, 还可以是 "assembly", 则使用资源文件 vltEngine.SetProperty(RuntimeConstants.RESOURCE_LOADER, "file"); // 模板存放目录 vltEngine.SetProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, Server.MapPath("~/Template/")); vltEngine.Init(); // 定义一个模板上下文 VelocityContext vltContext = new VelocityContext(); // 传入模板所需要的参数 vltContext.Put("Title", "NVelocity文件模板例子"); vltContext.Put("Count", listUsers.Count); vltContext.Put("ListUsers", listUsers); // 获取我们刚才所定义的模板,上面已设置模板目录, 此处用相对路径即可. Template vltTemplate = vltEngine.GetTemplate("users.xml"); // 定义一个字符串输出流 StringWriter vltWriter = new StringWriter(); // 根据模板的上下文,将模板生成的内容写进刚才定义的字符串输出流中 vltTemplate.Merge(vltContext, vltWriter); // 输出字符串流中的数据 Response.Write(vltWriter.GetStringBuilder().ToString());
终于搞定了,是不是很简单。
是滴,难倒是不难,只是……
别说了,不光你觉得烦,我都觉的有点烦人了,还是原来直接拼接字符串容易呀!
话虽这么说,但在真正的项目中还是会带来很多好处的,包括易维护性(老兄,写代码时,多替以后维护你代码的哥们着想着想呀。说不定那位哥们有一天会找上你家门找你算账的哦!有那么惨吗?维护的人知道有多惨,但你不知道维护的人有多惨……)。
代码包中包含了一个调用已编译dll中的模板的例子(user.xml模板已经被编译进dll中了,这样带来的好处是:以后你只需要将修改的dll覆盖掉原来的dll就可以了,大哥,组件化开发呀),代码差不多,我就不多说了,该说的我都说了,该你看了吧!
原文:http://depravedangel.iteye.com/blog/335598