Freemarker: iterating nested list in hash

21,416

Solution 1

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).

Solution 2

Try the following:

<#list groups?keys as groupKey>
    ${groupKey}
    <#list groups.get(groupKey) as item>
        ${item}
    </#list>
</#list>
Share:
21,416
pat.inside
Author by

pat.inside

Updated on February 10, 2020

Comments

  • pat.inside
    pat.inside over 4 years

    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)