FreeMarker一篇通1大家看文章标题就应该知道,我想用一篇文章,把大家从对freemaker的陌生直接带入到比较深入的境界,所以不想说一些基础性的东西,如果大家不习惯我的表达方法,大可通过google去找习惯于自己阅读方式的相关文章。 我用过velocity,最近才用freemaker,才知道我以前的选择是错了,因为velocity不支持过程的调用,所以我为velocity增加了很多的东西,写了很多代码,而且脚本也累赘得要命。freemaker首先吸引我的是它强大的过程调用和递归处理能力,其次则是xml风格的语法结构有着明显的边界,不象velocity要注意段落之间要留空格。所以我建议大家直接使用Freemaker,虽然freemaker没有.net版本,我想不嵌入程序中使用的话,freemaker是绝对的首选。(题外话,谁有兴趣移植一个NFreeMaker?) 在使用之前我们先要设置运行环境,在使用Freemaker的时候,我们需要下载相关的程序: 其中fmpp是一个freemaker的辅助工具,有了它,我们可以实现更多的功能。以下例子必须fmpp辅助。 这里我们首先提出问题。大家看如下的一个xml文件,虽然freemaker的能力不仅在于处理xml文件,但是用xml作为例子更直观一些: xml version='1.0' encoding="gb2312" ?>
<types xmlns:xsi="http://www./2001/XMLSchema-instance" xmlns="urn:DruleForm-Lite.xsd"> <type name="Type1" > <labels> <label lang="zh-CN" value="投保单"/> labels> <field name="Field11" type="Float" lbound="1" ubound="1" > <labels> <label lang="zh-CN" value="投保单ID"/> labels> field> <field name="Field12" type="String" lbound="1" ubound="*"/> <field name="Field13" type="Integer" lbound="1" ubound="*"/> <field name="Field14" type="Type2" lbound="1" ubound="*"> <type name="Type2"> <field name="Field21" type="String" lbound="1" ubound="*"/> <field name="Field22" type="Integer" lbound="1" ubound="*"/> type> field> <field name="Field15" type="InsuranceProduct" lbound="1" ubound="*"/> <type> <type name="Type3"> <field name="Field31" type="Type1" lbound="1" ubound="*" /> type> types> [代码1] 1<#ftl ns_prefixes={"ns": "urn:DruleForm-Lite.xsd"}>
2<#-- 定义xml namespace,以便在以下代码中使用,注意,ftl指令必须使用单独的行 --> 3<@pp.setOutputEncoding encoding="gb2312" /> <#-- 使用fmpp提供的函数来设置输出编码 --> 4 5<#recurse doc> <#-- 根入口,代码1部分的xml存放在变量doc中,doc变量的填充由fmpp根据config.fmpp中的配置进行 --> 6 7<#macro "ns:types"> <#-- xslt风格的匹配处理入口 --> 8<#recurse> <#-- 直接进行types节点内的匹配 --> 9#macro> 10 11<#macro "ns:type"> <#-- 匹配type节点 --> 12 class ${.node.@name} <#-- 其中.node是保留字,表示当前节点,引用的@name是xslt风格 --> 13 { 14 <#recurse> <#-- 继续匹配 --> 15 } 16#macro> 17 18<#macro "ns:field"> 19 public ${.node.@type} ${.node.@name}; 20#macro> 21 22<#macro @element> <#-- 所有没有定义匹配的节点到这里处理 --> 23#macro> 24 25 [代码2] 我们使用的配置文件设置如下: sourceRoot: src
outputRoot: out logFile: log.fmpp modes: [ copy(common/**/*.*, resource/*.*) execute(*.ftl) ignore(templates/*.*, .project, **/*.xml, xml/*.*, *.js) ] removeExtensions: ftl sourceEncoding: gb2312 data: { doc: xml(freemaker.xml) } [代码3] 然后我们在dos模式下运行指令: 最后的输出结果是这样的,存放在文件out\freemaker.中: class Type1
{ public Float Field11; public String Field12; public Integer Field13; public Type2 Field14; public Float Field15; } class Type3 { public Type1 Field31; } [代码4] 先来解释一下freemaker的基本语法了, [表1] <#macro "ns:field">
public ${.node.@type} ${.node.@name}; <#recurse > <#-- 深入处理子节点 --> #macro> [代码5] 结果输出文件中的内容就变为如下: class Type1
{ public Float Field11; public String Field12; public Integer Field13; public Type2 Field14; class Type2 { public String Field21; public Integer Field22; } public Float Field15; } class Type3 { public Type1 Field31; } [代码6] 如果各位有意向把Type2提到跟Type1和Type3同一级别的位置,那么我们要继续修改代码了。把代码2的 <#recurse doc>行(第5行)修改成如下: <#assign inner_types=pp.newWritableHash()> <#-- 调用fmpp功能函数,生成一个可写的hash -->
<#recurse doc> <#-- 根入口,代码1部分的xml存放在变量doc中,doc变量的填充由fmpp根据config.fmpp中的配置进行 --> <#if inner_types?size gt 0 > <#-- 如果存放有类型 --> <#list inner_types?values as node> <#-- 遍历哈西表的值 --> <#visit node> <#-- 激活相应的macro处理,类似于xslt的apply-template。大家把visit改成recurse看一下不同的效果 --> #list> #if> [代码7] 同时把macro ns:field(第18行)修改成如下: <#macro "ns:field">
public ${.node.@type} ${.node.@name}; <#if .node["ns:type"]?has_content > <#-- 如果当前节点下存在type节点 --> <#local t = .node["ns:type"] > <@pp.set hash=inner_types key="${t.@name}" value=t /> <#-- 哈西表中增加内容,key为嵌套类型的name属性,value为该类型节点 --> #if> #macro>
|
|