问题 Freemarker:如何使用枚举作为键来迭代Map


以下代码不起作用,因为Freemarker似乎将[]中的表达式的值转换为String,然后将其用作键,这不是实际预期的。

准备模板模型:

Map<MyEnum, Object> myMap;
myMap.put(MyEnum.FOO, "Foo");
myMap.put(MyEnum.BAR, "Bar");
templateModel.put("myMap", myMap);

my.ftl:

<#list myMap?keys as key>
    <#assign value = myMap[key]>
    <li>${key} = ${value}</li>
</#list>

在里面 Freemarker文档 它描述了如何访问Enum本身,但我没有找到任何关于如何使用Enum作为键从哈希值中获取值。

谢谢。


4374
2018-03-24 16:29


起源



答案:


解释一下 Freemarker文档常见问题解答 对此,

您不能在myMap [key]表达式中使用非字符串键。你可以使用方法!

因此,您可以创建一个bean,为您提供到达Java EnumMap的方法(即)。然后用你的mapp实例化这个bean,并将bean放在你的Model中。

class EnumMap
{
    HashMap<MyEnum, String> map = new HashMap<MyEnum, String>();

    public String getValue(MyEnum e)
    {
        return map.get(e);
    }    
    ..constructor, generics, getters, setters left out.
}

我对你想要达到的目标有点困惑。如果您只需要列出枚举值(或者每个值的显示值)。有一种更容易的方法来做到这一点。

我看到这个问题解决的一种方法是在Enum实例上放置一个显示值。

enum MyEnum 
{   FOO("Foo"), 
    BAR_EXAMPLE("Bar Example"); 
    private String displayValue;

    MyEnum(String displayValue)
    {
        this.displayValue = displayValue;
    }

    public String getDisplay()
    {
        return displayValue;
    }
}

这允许您将Enum本身放入您的配置中,并迭代所有实例。

SimpleHash globalModel = new SimpleHash();
TemplateHashModel enumModels = BeansWrapper.getDefaultInstance().getEnumModels();
TemplateHashModel myEnumModel = (TemplateHashModel) enumModels.get("your.fully.qualified.enum.MyEnum");

globalModel.put("MyEnum", myEnumModel);
freemarkerConfiguration.setAllSharedVariables(globalModel);

然后你可以遍历枚举,

<#list MyEnum?values as item>
    ${item.display}
</#list> 

12
2018-03-25 23:25



你应该接受这个答案,因为它帮助了我! - Amir Raminfar
但是当我想使用枚举作为键访问地图时,它不起作用。 mymap [MyEnum.SOMETHING]不起作用是有意义的,但我也不能使用mymap.get(MyEnum.SOMETHING)! - Wouter Lievens
请注意,这不再是最简单的解决方案。看到我的回答。 - ddekany
@Wouter Lievens: mymap.get() 只适用于某种情况 object_wrapper 设定值(纯净 BeansWrapper 同 simpleMaps=false),但强烈建议不要使用该值,加上改变 object_wrapper 从根本上来说,现有的应用程序可以很容易地打破它。无论如何,请参阅我的答案,以获得更“便携”的解决方案。 - ddekany


答案:


解释一下 Freemarker文档常见问题解答 对此,

您不能在myMap [key]表达式中使用非字符串键。你可以使用方法!

因此,您可以创建一个bean,为您提供到达Java EnumMap的方法(即)。然后用你的mapp实例化这个bean,并将bean放在你的Model中。

class EnumMap
{
    HashMap<MyEnum, String> map = new HashMap<MyEnum, String>();

    public String getValue(MyEnum e)
    {
        return map.get(e);
    }    
    ..constructor, generics, getters, setters left out.
}

我对你想要达到的目标有点困惑。如果您只需要列出枚举值(或者每个值的显示值)。有一种更容易的方法来做到这一点。

我看到这个问题解决的一种方法是在Enum实例上放置一个显示值。

enum MyEnum 
{   FOO("Foo"), 
    BAR_EXAMPLE("Bar Example"); 
    private String displayValue;

    MyEnum(String displayValue)
    {
        this.displayValue = displayValue;
    }

    public String getDisplay()
    {
        return displayValue;
    }
}

这允许您将Enum本身放入您的配置中,并迭代所有实例。

SimpleHash globalModel = new SimpleHash();
TemplateHashModel enumModels = BeansWrapper.getDefaultInstance().getEnumModels();
TemplateHashModel myEnumModel = (TemplateHashModel) enumModels.get("your.fully.qualified.enum.MyEnum");

globalModel.put("MyEnum", myEnumModel);
freemarkerConfiguration.setAllSharedVariables(globalModel);

然后你可以遍历枚举,

<#list MyEnum?values as item>
    ${item.display}
</#list> 

12
2018-03-25 23:25



你应该接受这个答案,因为它帮助了我! - Amir Raminfar
但是当我想使用枚举作为键访问地图时,它不起作用。 mymap [MyEnum.SOMETHING]不起作用是有意义的,但我也不能使用mymap.get(MyEnum.SOMETHING)! - Wouter Lievens
请注意,这不再是最简单的解决方案。看到我的回答。 - ddekany
@Wouter Lievens: mymap.get() 只适用于某种情况 object_wrapper 设定值(纯净 BeansWrapper 同 simpleMaps=false),但强烈建议不要使用该值,加上改变 object_wrapper 从根本上来说,现有的应用程序可以很容易地打破它。无论如何,请参阅我的答案,以获得更“便携”的解决方案。 - ddekany


接受的答案不是自2.3.22以来最简单的解决方案。而 myMap[key] 仍然假设一个字符串键(请参阅FAQ条目原因),现在可以使用了 myMap?api.get(key) 作为一种解决方法。它需要一些配置:

  • 首先, ?api 默认情况下不允许,因此您需要设置 api_builtin_enabled (Configuration.setAPIBuiltinEnabled(boolean))设置为 true
  • 然后, object_wrapper (Configuration.setObjectWrapper(ObjectWrapper))used需要支持这个功能(暴露API)。如果你没有设置 object_wrapper 在任何地方,然后只是增加 incompatible_improvements 设置( Configuration 构造函数参数,或 Configuration.setIncompatibleImprovements(Version))到2.3.22解决了这个问题。如果你设置了 object_wrapper (即,你覆盖默认值),它是一个 DefaultObjectWrapper 例如,然后注意 DefaultObjectWrapper 有它自己的 incompatibleImprovements 必须设置为2.3.22的属性。如果你使用纯净 BeansWrapper (不推荐!)然后你必须什么都不做,因为它总是支持这个功能。

4
2018-03-01 14:26