提取Apache Velocity模板中的变量

Apache Velocity是一个用于简化Java应用程序开发的开源模板引擎。它允许开发人员使用模板文件来生成文本文件,例如HTML网页、XML文件、邮件、SQL语句等。Velocity模板文件包含静态文本和用于填充数据的变量、条件语句和循环语句等。

目前Apache Velocity最新的版本为 2.3,最后更新时间为2021年3月7日。目前已经停止维护。

1
2
3
4
5
6
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>

Apache Velocity中引用变量的几种方式

  • ${var}
  • $var
  • $!{var}
  • $!var

提取变量

以下面这个模板为例:

1
#if($todoType == 1)aa#{else}bb#end 和 ${okk} $name ${serviceData.key} $!serviceData.value #userName($serviceData.operationManager)

此模板中使用了多种变量引用方式,还包含了条件判断和指令的使用。涵盖了模板使用的大部分场景。

其中 #userName 为一个自定义指令。

具体实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Slf4j
public class VelocityVariableExtractor {
private static final RuntimeInstance RI = new RuntimeInstance();

private VelocityVariableExtractor() {
}

public static Set<String> extractVariables(String templateContent) {
try {
Template template = new Template();
StringReader reader = new StringReader(templateContent);
SimpleNode node = RI.parse(reader, template);
VariableExtractingVisitor visitor = new VariableExtractingVisitor();
node.jjtAccept(visitor, null);
return visitor.getVariables();
} catch (ParseException e) {
log.error(e.getMessage(), e);
return Collections.emptySet();
}
}

@Getter
private static class VariableExtractingVisitor extends BaseVisitor {
private final Set<String> variables = new HashSet<>();

@Override
public Object visit(ASTReference node, Object data) {
variables.add(node.literal());
return super.visit(node, data);
}
}

}

这里用到了访问这模式。访问者的结构如下:

$2ParserVisitorvisit(node:SimpleNode, data:Object): Objectvisit(node:ASTprocess, data:Object): Objectvisit(node:ASTReference, data:Object): ObjectBaseVisitorVariableExtractingVisitorvisit(node:ASTReference, data:Object): Object

ParserVisitor提供了很多访问方法,这里我们只用Object visit(ASTReference node, Object data);这个方法。ASTReference就是引用节点的类型,在遍历模板节点时所有引用类型的节点都会经过这个方法。

使用

1
2
3
4
5
6
7
8
public class Main {

public static void main(String[] args) {
String tpl = "#if($todoType == 1)aa#{else}bb#end 和 ${okk} $name ${serviceData.key} $!serviceData.value #userName($serviceData.operationManager)";
Set<String> vars = VelocityVariableExtractor.extractVariables(tpl);
System.out.println(StringUtils.join(vars, ","));
}
}

输出结果

1
$todoType,$name,${okk},${serviceData.key},$serviceData.operationManager,$!serviceData.value

去掉变量中的表达式前缀和后缀

从上面的结果中可以看出,提取的变量中都带了表达式的前缀和后缀。现在只需要对 VariableExtractingVisitor 稍作修改即可去掉这些前后缀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Getter
private static class VariableExtractingVisitor extends BaseVisitor {
private final Set<String> variables = new HashSet<>();

@Override
public Object visit(ASTReference node, Object data) {
String varStr = tripIdentifier(node.literal());
if (!isFunction(varStr)) {
variables.add(varStr);
}
return super.visit(node, data);
}

private static String tripIdentifier(String varStr) {
if (varStr.startsWith("$!{")) {
return varStr.replace("$!{", "").replace("}", "");
} else if (varStr.startsWith("${")) {
return varStr.replace("${", "").replace("}", "");
} else if (varStr.startsWith("$!")) {
return varStr.replace("$!", "");
} else {
return varStr.replace("$", "");
}
}

private static boolean isFunction(String varStr) {
return varStr.contains("(") && varStr.contains(")");
}
}

本例中使用 replace方法将这些标识替换为空串。

运行结果:

1
todoType,serviceData.value,name,serviceData.key,okk,serviceData.operationManager
作者

大扑棱蛾子(jaune162@126.com)

发布于

2024-02-29

更新于

2024-09-11

许可协议

评论