常用模板引擎对比分析

常用的模板引擎:

  • jinja
  • freemarker
  • velocity

基本能力是差不多的。只不过jinja是python实现,其他两个是java实现。

freemarker用法

<#--普通变量替换 -->
package ${classPath};

public class ${className} {

    public static void main(String[] args) {
        System.out.println("${helloWorld}");
    }

}

<#--函数 -->
第一个字母大写:${data?cap_first}
所有字母小写:${data?lower_case}
所有字母大写:${data?upper_case}
数值取整数:${floatData?int}

<#--列表操作 -->
获取集合的长度:${users?size}
<#if users??>
<#list users as user >
${user}
#list>
<#else>
${user!"变量为空则给一个默认值"}
#if>

<#--字典操作 -->
直接通过Key获取 Value值:${mapData.liping}
直接通过Key获取 Value值:${mapData['liping']}
通过Key遍历Map:
<#list mapData?keys as key>
${key} -> ${mapData[key]}
#list>
通过Value遍历Map:
<#list mapData?values as value>
${value}
#list>

注意几点:

  • 待替换的变量必须写成“${变量名}”形式,大括号不能省略
  • 函数调用使用?,而且支持哪些函数都是freemarker内置的

处理上述模板的java代码如下:

static void testFreeMarker() throws IOException, TemplateException {
        Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        Template template = configuration.getTemplate("src/main/resources/hello.ftl");

        Map dataMap = new HashMap();
        dataMap.put("classPath", "com.freemark.hello");
        dataMap.put("className", "AutoCodeDemo");
        dataMap.put("helloWorld", "HelloWorld!");

        dataMap.put("data", "abcd1234");
        dataMap.put("floatData", 12.34);

        List users = Arrays.asList("bob", "suzy");
        dataMap.put("users", users);

        Map m = new LinkedHashMap<>();
        m.put("liping", 30);
        m.put("xx1", 7);
        m.put("xx2", 56);
        dataMap.put("mapData", m);


        try(Writer writer = new FileWriter("hello.res"))
        {
            template.process(dataMap, writer);
        }
    }

Velocity用法

velocity语法参考:

https://www.ibm.com/developerworks/cn/java/j-lo-velocity1/

我们来写出与上述模板等价的velocity模板:

## 普通变量替换,与freemarker不同,大括号不是必须的
package ${classPath};

public class $className {

    public static void main(String[] args) {
        System.out.println("${helloWorld}");
    }

}

## 函数
第一个字母大写:${Helper.capitalize($data)}
所有字母小写:${data.toLowerCase()}
所有字母大写:${data.toUpperCase()}
数值取整数:${Helper.toInt($floatData)}

## 列表操作
获取集合的长度:${users.size()}
#if($users)
#foreach($user in $users)
${user}
#end
#else
${users}
#end

## 字典操作
直接通过Key获取 Value值:${mapData.liping}
直接通过Key获取 Value值:${mapData['liping']}
通过Key遍历Map#foreach($item in ${mapData.entrySet()})
${item.key} -> ${item.value}
#end
通过Value遍历Map#foreach($value in $mapData)
${value}
#end

与freemarker的差异:

  • 引用变量时大括号不是必须的,像$className在freemarker里就是无效的
  • 函数调用使用点号,且支持哪些函数使用的是java的反射机制,像字符串大写使用${data.toLowerCase()}
  • 列表、字典遍历使用#foreach
  • 可以传递一个类给velocity,在模板里调用这个类的静态方法,像“第一个字母大写”用到的{Helper.capitalize( {Helper.capitalize( data)},Helper是我们自己提供的一个类,capitalize是其静态方法。

处理velocity模板的java代码如下:

static void testVelocity() throws IOException {
        VelocityEngine ve = new VelocityEngine();
        ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
        ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
        ve.init();

        //这里要指定utf8编码,否则中文输出乱码
        org.apache.velocity.Template t = ve.getTemplate("hello.vtl", "utf-8");
        VelocityContext dataMap = new VelocityContext();

        dataMap.put("classPath", "com.freemark.hello");
        dataMap.put("className", "AutoCodeDemo");
        dataMap.put("helloWorld", "HelloWorld!");

        dataMap.put("data", "abcd1234");
        dataMap.put("floatData", 12.34);

        dataMap.put("Helper", Helper.class);

        List users = Arrays.asList("bob", "suzy");
        dataMap.put("users", users);

        Map m = new LinkedHashMap<>();
        m.put("liping", 30);
        m.put("xx1", 7);
        m.put("xx2", 56);
        dataMap.put("mapData", m);

        try(Writer writer = new FileWriter("hello_v.res"))
        {
            t.merge(dataMap, writer);
        }
    }

我们还要提供Helper辅助类,才能确保模板输出正确:

public final class Helper
{
    public static String capitalize(String s)
    {
        if (s == null || s.length() == 0)
        {
            return s;
        }

        char[] charArray = s.toCharArray();
        charArray[0] -= 32;
        return String.valueOf(charArray);
    }

    public static int toInt(double d)
    {
        return (int)d;
    }
}

注意:Helper类必须要声明为public,否则模板里无法访问到。

jinja用法

jinja语法可参考:

http://docs.jinkan.org/docs/jinja2/templates.html#builtin-globals

上述模板等价的jinja模板为:

{# 普通变量替换 #}
package {{classPath}};

public class {{className}} {

    public static void main(String[] args) {
        System.out.println("{{helloWorld}}");
    }

}

{# 函数 #}
第一个字母大写:{{data | capitalize}}
所有字母小写:{{data | lower}}
所有字母大写:{{data | upper}}
数值取整数:{{floatData | int}}

{# 列表操作 #}
获取集合的长度:{{users | length}}
{% if users %}
{% for user in users %}
{{user}}
{% endfor %}
{% else %}
{{users}}
{% endif %}

{# 字典操作 #}
直接通过Key获取 Value值:{{mapData.liping}}
直接通过Key获取 Value值:{{mapData['liping']}}
通过Key遍历Map:
{% for key in mapData.keys() %}
{{key}} -> {{mapData[key]}}
{% endfor %}
通过Value遍历Map:
{% for value in mapData.values() %}
{{value}}
{% endfor %}

与velocity、freemarker的差别:

  • 使用{{}}来引用变量
  • 使用过滤器来模拟函数调用

处理模板的python代码:

from jinja2 import Environment, FileSystemLoader

env = Environment(loader=FileSystemLoader('./', encoding='utf-8'))

template = env.get_template('hello.jtl')
cont = template.render(classPath='com.freemark.hello', className='AutoCodeDemo',
                       helloWorld=u'HelloWorld!',
                       data='abcd1234',
                       floatData=12.34,
                       users=["bob", "suzy"],
                       mapData={"liping" :30, "xx1":7, "xx2" : 56})

# print cont

with open('hello_j.res', 'w') as writer:
    writer.write(cont.encode('utf-8'))

总结

freemarker、jinja更像DSL,比较简洁,而velocity则像在写java代码,有点啰嗦。

你可能感兴趣的:(模板引擎)