Freemarker:如何使用枚举作为键来遍历Map

13
以下代码无法正常运行,因为Freemarker似乎会将[]内部表达式的值转换为字符串,然后将其用作键,这并不是实际预期的结果。
准备模板模型:
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文档中描述了如何访问枚举本身,但我没有找到有关如何使用枚举作为键从哈希中获取值的内容。
谢谢。
2个回答

12

根据Freemarker文档FAQ的解释,可以这样理解:

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

因此,你可以创建一个bean来提供访问Java EnumMap的方法。然后,只需使用你的映射实例化此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 MyEnum 
{   FOO("Foo"), 
    BAR_EXAMPLE("Bar Example"); 
    private String displayValue;

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

    public String getDisplay()
    {
        return displayValue;
    }
}
这使您能够将枚举本身放入配置中,并迭代所有实例。
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> 

3
当我想使用枚举作为键来访问地图时,它无法正常工作。使用 mymap[MyEnum.SOMETHING] 无法实现很有道理,但我也不能使用 mymap.get(MyEnum.SOMETHING)! - Wouter Lievens
请注意,这不再是最简单的解决方案。请参考我的答案。 - ddekany
@Wouter Lievens:mymap.get() 仅在某种 object_wrapper 设置值(使用纯 BeansWrappersimpleMaps=false)下起作用,但强烈不建议使用该值,而且在现有应用程序中根本更改 object_wrapper 很容易导致其崩溃。无论如何,请参阅我的答案以获得更“便携”的解决方案。 - ddekany

4
自2.3.22版本以后,被接受的答案不是最简单的解决方法。虽然myMap[key]仍然假定键为字符串(请参见FAQ条目),但现在可以使用myMap?api.get(key)作为一种解决方法。不过需要进行一些配置:
  • 首先,默认情况下禁用?api,因此需要将api_builtin_enabled (Configuration.setAPIBuiltinEnabled(boolean))设置为true
  • 然后,所使用的object_wrapper (Configuration.setObjectWrapper(ObjectWrapper))需要支持该功能(公开API)。如果没有在任何地方设置object_wrapper,则将incompatible_improvements设置(Configuration构造函数参数或Configuration.setIncompatibleImprovements(Version))增加到2.3.22即可解决。如果设置了object_wrapper(即覆盖了默认值)并且它是DefaultObjectWrapper实例,则请注意,DefaultObjectWrapper具有自己的 incompatibleImprovements 属性,必须设置为2.3.22。如果使用纯粹的BeansWrapper(不推荐!)则不需要做任何操作,因为它始终支持此功能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接