java - 在 freemarker-template 中使用宏进行递归类型检查
问题描述
我想递归遍历 LinkedHashMap 的键和值,并使用 apache freemarker 将它们打印到模板中。LinkedHashMap 包含作为字符串的键和作为对象的值。这些值可以是 LinkedHashMap、ArrayList 或 String。
以下 java 源代码返回我想使用 freemarker 创建的字符串。
private String printLinkedHashMap(LinkedHashMap<?, ?> map, int counter) {
if(map == null) {
return "";
}
StringBuilder sb = new StringBuilder();
Set<?> set = map.entrySet();
sb.append(getSpaces(counter) + "<ul>\n");
for (Object object : set) {
if(object instanceof Entry<?, ?>) {
Entry<?, ?> entry = (Entry<?, ?>) object;
sb.append(getSpaces(counter) + "<li>" + entry.getKey() + " = ");
if(entry.getValue() instanceof LinkedHashMap<?, ?>) {
sb.append("</li>\n" + printLinkedHashMap((LinkedHashMap<?, ?>) entry.getValue(), counter+1));
} else if(entry.getValue() instanceof ArrayList<?>){
ArrayList<?> listOfValues = (ArrayList<?>) entry.getValue();
sb.append("</li>" + printArrayList(listOfValues, counter+1));
} else {
sb.append(entry.getValue().toString() + "</li>\n");
}
}
}
sb.append(getSpaces(counter) + "</ul>\n");
return sb.toString();
}
private String printArrayList(ArrayList<?> listOfValues, int counter) {
StringBuilder sb = new StringBuilder();
if(listOfValues.size() > 1) {
for (int i = 0; i < listOfValues.size(); i++) {
sb.append('\n' + getSpaces(counter));
sb.append("<li>" + listOfValues.get(i).toString() + "</li>");
}
} else {
sb.append(listOfValues.get(0).toString());
}
return sb.toString();
}
输出是:
<ul>
<li>defaultVar = </li>
<ul>
<li>subdefaultVar = </li>
<ul>
<li>subsubdefaultVar = defaultValue</li>
<li>subsubdefaultVariable = defaultValue2</li>
</ul>
<li>anothersubdefaultVar = </li>
<ul>
<li>anothersubsubdefaultVar = anotherdefaultValue</li>
<li>anothersubsubdefaultVariable = anotherdefaultValue2</li>
</ul>
</ul>
<li>defaultVariable = Defaultshort</li>
</ul>
freemarker 模板包含以下部分。
<#list map.entrySet() as entry>
<@printLinkedHashMap entry/>
<#macro printLinkedHashMap obj>
<#if obj??>
<ul>
<#if obj.key??>
<#if obj.key?is_string>
<li>${obj.key} =
</#if>
</#if>
<#if obj.value??>
<#if obj.value?is_hash_ex>
</li>
<#list obj.value.entrySet() as entry>
<@printLinkedHashMap entry/>
</#list>
<#elseif obj.value?is_collection>
</li>
<ul>
<#list obj.value as current>
<li>${current.value}</li>
</#list>
</ul>
<#elseif obj.value?is_string>
${obj.value}</li>
</#if>
</#if>
</ul>
</#if>
</#macro>
</#list>
当我使用此模板时,将显示以下错误:
The following has evaluated to null or missing:
==> obj.value.entrySet [in template "template.ftl" at line 55, column 26]
----
Tip: It's the step after the last dot that caused this error, not those before it.
----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----
----
FTL stack trace ("~" means nesting-related):
- Failed at: #list obj.value.entrySet() as entry [in template "template.ftl" in macro "printLinkedHashMap" at line 55, column 19]
- Reached through: @printLinkedHashMap entry [in template "template.ftl" in macro "printLinkedHashMap" at line 56, column 21]
- Reached through: @printLinkedHashMap entry [in template "template.ftl" in macro "printLinkedHashMap" at line 56, column 21]
- Reached through: @printLinkedHashMap entry [in template "template.ftl" at line 41, column 11]
----
该错误表明“obj.value.entrySet()”似乎为空。但我不明白为什么会发生这种情况以及如何解决这个问题。
解决方案
我为该问题创建了一个解决方案。如果其他人需要它,我的 ftl 文件现在包含以下宏:
<#macro printLinkedHashMap entry>
<#if entry.key??>
<ul>
<#if entry.key??>
<#if entry.key?is_string>
<li>${entry.key} =
</#if>
</#if>
<#if entry.value??>
<#if entry.value?is_hash_ex>
<#if (entry.value.entrySet())??>
<#list entry.value.entrySet() as newEntry>
<#if newEntry.key??>
<#if newEntry.value??>
<@printLinkedHashMap newEntry/>
</#if>
</#if>
</#list>
<#elseif entry.value?is_collection>
<ul>
<#list entry.value as currentValue>
<li>currentValue</li>
</#list>
</ul>
<#else>
${entry.value}</li>
</#if>
</li>
</#if>
</#if>
</ul>
</#if>
</#macro>
解决方案
最可能的原因是HashMap
您的结构中某处有 a 而不是LinkedHashMap
.
由HashMap
freemarker 包装成 a SimpleHash
,它没有entrySet()
方法,这会导致错误obj.value.entrySet has evaluated to null or missing
。
请注意,您应该更喜欢使用map?keys
来迭代您的地图。它适用于HashMap
和LinkedHashMap
(并以正确的顺序返回键LinkedHashMap
)。