Java freemarker模板中宏的递归类型检查

Java freemarker模板中宏的递归类型检查,java,recursion,freemarker,typechecking,Java,Recursion,Freemarker,Typechecking,我想递归遍历LinkedHashMap的键和值,并使用ApacheFreeMarker将它们打印到模板中。 LinkedHashMap包含作为字符串的键和作为对象的值。这些值可以是LinkedHashMap、ArrayList或字符串 下面的java源代码返回我想使用freemarker创建的字符串 private String printLinkedHashMap(LinkedHashMap<?, ?> map, int counter) { if(map == n

我想递归遍历LinkedHashMap的键和值,并使用ApacheFreeMarker将它们打印到模板中。 LinkedHashMap包含作为字符串的键和作为对象的值。这些值可以是LinkedHashMap、ArrayList或字符串

下面的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();
    }
私有字符串printLinkedHashMap(LinkedHashMap映射,int计数器){
if(map==null){
返回“”;
}
StringBuilder sb=新的StringBuilder();
Set=map.entrySet();
sb.append(getSpaces(counter)+“
    \n”); 用于(对象:集){ if(条目的对象实例){ 条目=(条目)对象; sb.append(getSpaces(counter)+“
  • ”+entry.getKey()+”=”); if(LinkedHashMap的entry.getValue()实例){ sb.append(“
  • \n”+printLinkedHashMap((LinkedHashMap)entry.getValue(),counter+1)); }else if(entry.getValue()instanceof ArrayList){ ArrayList listOfValues=(ArrayList)entry.getValue(); sb.追加(“”+printArrayList(值列表,计数器+1)); }否则{ sb.append(entry.getValue().toString()+“\n”); } } } sb.append(getSpaces(counter)+“
\n”); 使某人返回字符串(); } 私有字符串printArrayList(ArrayList listOfValues,int计数器){ StringBuilder sb=新的StringBuilder(); if(listOfValues.size()>1){ 对于(int i=0;i”+listOfValues.get(i).toString()+””); } }否则{ sb.append(listOfValues.get(0.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>
  • defaultVar=
    • subdefaultVar=
      • subsubdefaultVar=defaultValue
      • subsubdefaultVariable=defaultValue2
    • 另一个SubDefaultVar=
      • AnotherSubDefaultVar=anotherdefaultValue
      • AnotherSubDefaultVariable=anotherdefaultValue2
  • defaultVariable=Defaultshort
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>

  • ${obj.key}=
    • ${current.value}
    ${obj.value}
使用此模板时,将显示以下错误:

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[在第55行第26列的模板“template.ftl”中]
----
提示:导致此错误的是最后一个点之后的步骤,而不是之前的步骤。
----
提示:如果已知失败的表达式在法律上引用了有时为null或缺少的内容,请指定一个默认值,如myOptionalVar!myDefault,或在出现时使用缺少时使用。(这些仅包括表达式的最后一步;要覆盖整个表达式,请使用括号:(myOptionalVar.foo)!myDefault,(myOptionalVar.foo)??
----
----
FTL堆栈跟踪(“~”表示嵌套相关):
-在以下位置失败:#将obj.value.entrySet()列为条目[在第55行第19列的宏“printLinkedHashMap”的模板“template.ftl”中]
-通过:@printLinkedHashMap条目[在第56行第21列的宏“printLinkedHashMap”的模板“template.ftl”中]
-通过:@printLinkedHashMap条目[在第56行第21列的宏“printLinkedHashMap”的模板“template.ftl”中]
-通过:@printLinkedHashMap条目[在模板“template.ftl”第41行第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>

  • ${entry.key}=
    • 当前值
    ${entry.value}

最可能的原因是,在您的结构中的某个地方有一个
HashMap
,而不是
LinkedHashMap

HashMap
被freemarker包装成一个
SimpleHash
,它没有
entrySet()
方法,导致错误
obj.value.entrySet的计算结果为null或缺少


请注意,您应该更喜欢使用
map?键
在地图上迭代。它将在
HashMap
LinkedHashMap
上工作(并按
LinkedHashMap
的正确顺序返回键).

最可能的原因是,在您的结构中某处存在一个
HashMap
,而不是
LinkedHashMap

HashMap
被freemarker包装成一个
SimpleHash
,它没有
entrySet()
方法,导致错误
obj.value.entrySet的计算结果为null或缺少

请注意,您应该更喜欢使用
map?键
在您的映射上进行迭代。这将在
HashMap上都起作用<