Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Freemarker: iterating nested list in hash

I want to iterate a List nested in a Map, the data structure is like:

Map<Integer, List<Integer>> groups = new TreeMap<>()
// Some code else to put values into groups ...

Freemarker template:

<#list groups?keys as groupKey>
    ${groupKey}    // It's OK here.
    <#list groups[groupKey] as item>  // Exception threw here, detail message is pasted below
        ${item}
    </#list>
</#list>

Detail exception message:

FreeMarker template error: For "...[...]" left-hand operand: Expected a sequence or string or something automatically convertible to string (number, date or boolean), but this evaluated to an extended_hash (wrapper: f.t.SimpleHash): ==> groups

So, what is the problem?

P.S.

I have tried groups.get(groupKey) instead of groups[groupKey], it throws a new Exception stack:

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
java.lang.String.compareTo(String.java:108)
java.util.TreeMap.getEntry(TreeMap.java:346)
java.util.TreeMap.get(TreeMap.java:273)
freemarker.template.SimpleHash.get(SimpleHash.java:160)
freemarker.core.Dot._eval(Dot.java:40)
freemarker.core.Expression.eval(Expression.java:76)
like image 350
pat.inside Avatar asked Dec 03 '14 05:12

pat.inside


2 Answers

The problem in the original question is that FTL's hash type is not like Map. It's a collection of "variables", that is, the keys must be String-s. (Even that ?keys works is a glitch in BeansWrapper... though now it comes handy.) Since the key is a number, FTL assumes that you want to get an item from a sequence (a List or array), or that you want to get a character from a string, hence the original error message.

The solution is using the Java API-s, like get in Dev-an's answer. (On the long term FTL meant to introduce the map type, so all this problems with non-string keys will end, but who knows when that will be...)

Update: Since 2.3.22 there's ?api to access the Java API of objects, like myMap?api.get(nonStringKey). However, it's by default not allowed (see the api_builtin_enabled configuration setting and more in the Manual: http://freemarker.org/docs/ref_builtins_expert.html#ref_buitin_api_and_has_api). Also note that as Java maps are particular about the numerical type, if the key is not an Integer coming from a Java, you have to use myMap?api.get(myNumericalKey?int).

like image 93
ddekany Avatar answered Nov 04 '22 16:11

ddekany


Try the following:

<#list groups?keys as groupKey>
    ${groupKey}
    <#list groups.get(groupKey) as item>
        ${item}
    </#list>
</#list>
like image 3
Dev-an Avatar answered Nov 04 '22 17:11

Dev-an