Freemarker中如何进行Json转化
缘起
之前在开发过程中,freemarker的解析经常遇见一些问题,这里将这些问题做一下记录.
比如,如下错误,是因为freemarker模板中出现了一个全角的中文空格,导致解析出现失败.
Error:(1, 1) java: 非法字符: '\ufeff'
需求
需求是,从freemarker中构造出一个Json,然后将这个json进行url编码,放到一个url后面,用户点击这个url,则立即可以跳转到指定的页面,页面再根据这个参数做相应的解析.
实现
在freemarker中,是通过拼凑Json字段进行处理的.在拼凑Json字段的过程中,会涉及字段的转义:
event.params.group?j_string
以及urlencode:
<#assign url_param="search=${search?url}" />
还有特别重要的是,有些字段需要判空,判空有好几种形式:
<#--条件判空-->
<#if event.params.processName?? >
<#assign processName = event.params.processName?j_string />
</#if>
<#--感叹号判空-->
<#assign filePath = (event.params.filePath!"")?j_string />
<#--为空,则展示为空串-->
${(grouplist)!}
整体例子:
<#macro detailLinkParams aggregateAgentEvents>
<#setting url_escaping_charset='utf8'/>
<#if (aggregateAgentEvents?size>0) >
<#--连接进程-->
<#assign processName="" />
<#--业务组-->
<#assign group=[] />
<#--主机名-->
<#assign hostname="" />
<#--端口-->
<#assign targetPort="" />
<#--目标主机-->
<#assign targetIpLike="" />
<#--主机IP-->
<#assign ip="" />
<#--时间区间-->
<#assign time_max = aggregateAgentEvents[0].params.dataTime/>
<#assign time_min = aggregateAgentEvents[0].params.dataTime/>
<#list aggregateAgentEvents as event>
<#if event.params.group?? && !(group?seq_contains(event.params.group?j_string)) >
<#assign group = group + [event.params.group?j_string] />
</#if>
<#if event.params.processName?? >
<#assign processName = event.params.processName?j_string />
</#if>
<#if event.params.hostname?? >
<#assign hostname = event.params.hostname?j_string />
</#if>
<#if event.params.targetPort?? >
<#assign targetPort = event.params.targetPort?j_string />
</#if>
<#if event.params.targetIp?? >
<#assign targetIpLike = event.params.targetIp?j_string />
</#if>
<#if event.params.displayIp?? >
<#assign ip = event.params.displayIp?j_string />
</#if>
<#if (time_max < event.params.dataTime) >
<#assign time_max = event.params.dataTime />
</#if>
<#if (time_min > event.params.dataTime) >
<#assign time_min = event.params.dataTime />
</#if>
</#list>
<#assign time_max = time_max + 1/>
<#assign time_min = time_min - 1/>
<#assign time="{\"min\":\"${(time_min*1000)?number_to_datetime?string('yyyy-MM-dd HH:mm:ss')}\",\"max\":\"${(time_max*1000)?number_to_datetime?string('yyyy-MM-dd HH:mm:ss')}\"}"/>
<#assign grouplist = group?join(",") />
<#if (aggregateAgentEvents?size==1) >
<#assign search="{\"processName\":\"${(processName)!}\",\"createTime\":${time},\"group\":[${(grouplist)!}],\"ip\":\"${(ip)!}\",\"targetIpLike\":\"${(targetIpLike)!}\",\"targetPort\":${(targetPort)!},\"hostname\":\"${(hostname)!}\"}"/>
<#else>
<#assign search="{\"processName\":\"${(processName)!}\",\"createTime\":${time},\"group\":[${(grouplist)!}],\"ip\":\"${(ip)!}\",\"targetIpLike\":\"${(targetIpLike)!}\",\"targetPort\":${(targetPort)!},\"hostname\":\"${(hostname)!}\"}"/>
</#if>
<#assign url_param="search=${search?url}" />
<#else>
<#assign url_param="a=1"/>
</#if>
${url_param}</#macro>
额外的问题
FreeMarker遍历集合集合时貌似有个要求,集合本身必须是Collection的子类;如果将jackson的JsonNode对象,扔给Freemarker则可能会有问题,例如:
{
"vuls": [
{
"vuln": 1,
"port": 6379,
"title": "xxx",
"checkid": 7,
"msg": "xxx!",
"hacked_file": [
{
"info": "xxx",
"path": "xxx"
}
],
"pid": 1351
}
]
}
如果通过如下模板进行解析,则会出现问题,因为json对象中的数组,无法用freemarker中的list遍历,这点很奇怪。
XXX :
<#list vuls as vul>
<#list vul.hacked_file as item>
xx:${item.path},xxx:${item.info}
</#list>
</#list>
但是可以通过把JsonNode当做字符串处理,在freemarker中用eval进行处理,则没问题:
<#function parseJSON json>
<#local null = 'null'> <#-- null is not a keyword in FTL -->
<#return json?eval>
</#function>
<#assign jsonObj=parseJSON(vuls)/>
XXX :
<#list jsonObj as vul>
<#list vul.hacked_file as item>
xx:${item.path},xxx:${item.info}
</#list>
</#list>
参考资料
https://stackoverflow.com/questions/46154391/how-can-i-iterate-a-collection-twice-in-freemarker#
https://stackoverflow.com/questions/17778844/evaluate-json-with-null-value-using-freemarker
https://stackoverflow.com/questions/12708162/how-to-get-json-into-a-freemarker-template-ftl
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!