FreeMarker 复杂集合
Posted
技术标签:
【中文标题】FreeMarker 复杂集合【英文标题】:FreeMarker Complex Collection 【发布时间】:2013-08-29 05:45:19 【问题描述】:我有一个 Map<Long, List<Map<String, Object>>> typeAndKno
的 HashMap,在 FreeMarker 页面中,我得到这个地图的内容是这样的:
<#list typeAndKno?keys as typeId>
$typeAndKno.get(typeId).get(0).get('TYPE_NAME')
<#list typeAndKno.get(typeId) as kno>
$kno.get('KNOWLEDGE_ID')
</#list>
</#list>
此代码在 Struts2 中运行良好,但移至 Spring MVC 后,代码失败。我终于把代码改成了这样:
<#list typeAndKno?keys as typeId>
$typeAndKno[typeId]?first['TYPE_NAME']
<#list typeAndKno[typeId?string] as kno>
$kno['KNOWLEDGE_ID']
</#list>
</#list>
这两段代码有什么区别?有没有办法让第一段代码在 Spring MVC 中工作?
【问题讨论】:
我猜是因为 OGNL。 @AleksandrM 我看不懂,OGNL 为它做了什么? 因为在您的第二个示例中,您使用的是 freemarker 内置插件。 @AleksandrM 也就是说,OGNL 做了一些方便使用 freemarker 的事情。我想知道如何使它像spring MVC中的struts 2一样方便 不同之处在于 FreeMarkerobject_wrapper
设置。 Spring下面的那个看起来更好(但不要写kno['KNOWLEDGE_ID']
之类的,kno.KNOWLEDGE_ID
更简洁)。只是,它真的有效吗?就像如果typeId
很长,那么typeAndKno[typeId]
不应该工作。
【参考方案1】:
更新:
从 2.3.22 开始,有一个更简单且无中断的解决方案:配置 FreeMarker 以便 ?api
工作,然后您可以使用 Map
的 Java API,其中密钥不是 String
-s。请参阅this FAQ entry 或this answer 了解更多详情。
现在强烈反对使用 Strut 的 FreeMarker 设置。当然,当时,当他们这样做时,这可能是最合理的解决方法,但暂时不再是,尤其是从 2.3.22 开始。
旧答案(过时):
您从模板中查看 Java 对象的方式取决于所使用的 ObjectWrapper
,这是一个 FreeMarker 配置设置。根据您的示例,Struts 使用 BeansWrapper
及其默认设置,而 Spring 可能使用 DefaultObjectWrapper
。所以这导致了差异。我不建议使用任何一个,因为:
使用 BeansWrapper
及其默认设置 Map
键与方法名称混合,方法名称具有优先权。当然,您可以安全地使用myMap.get(key)
来解决这个问题,但是与myMap.foo
相比,myMap.get('foo')
实在是太可怕了,它只会在您没有名为foo
的方法时才有效。同样?keys
将返回一个混合的真实键和方法名......这是一团糟。
使用DefaultObjectWrapper
,您可以安全地编写myMap.foo
,但您将无法获取具有非字符串键的实体,因为myMap[key]
仅支持字符串,而您没有@987654339 @ 了。
所以我通常与 FreeMarker 一起使用的是 bw = new BeansWrapper(); bw.setSimpleMapWrapper(true)
。这样Map
-s 的方法就不可见了,就像DefaultObjectWrapper
一样,所以你可以安全地使用myMap.foo
和myMap[key]
。但是,如果您很少需要使用非字符串键获取某些内容,则可以使用myMap(nonStringKey)
(是的,使用()
而不是[]
)。最后一个不适用于DefaultObjectWrapper
。 (希望 FreeMarker 2.4 能解决这个乱七八糟的非字符串键问题,但它不会很快就出来......)
那么接下来的问题是如何用Spring设置对象包装器。我不在家。据我所知,你有一个 FreeMarkerConfigurer
bean,它有一个 freemarkerSettings
属性,这是一个 Properties
对象,最终传递给 FreeMarker 的基于 Properties
的配置 API。因此,您应该可以添加一个 object_wrapper
属性,该属性引用要使用的 ObjectWrapper
的类名(或者它可能只是 beans
以使用默认的 BeansWrapper
实例,就像 Struts 一样)。问题是,基于属性的 API 相当有限,因此您不能同时创建和配置(调用 setSimpleMapWrapper
)BeansWrapper
。当然,您可以在 spring 配置文件中执行此操作,但我看不到将其注入 FreeMarkerConfigurer
的方法,除非您将 whole freemarker.template.Configuration
对象创建为 bean,并且将其注入到 FreeMarkerConfigurer
中,并带有 configuration
属性。所以也许最简单的解决方法是扩展BeansWrapper
以覆盖默认的simpleMapWrapper
,然后通过object_wrapper
引用该扩展类的类。 (以防有人稍后阅读此内容,FreeMarker 2.3.21 可能会扩展属性配置 API,以便您可以将 object_wrapper
设置为 BeansWrapper() simpleMapWrapper = true
。)
【讨论】:
以上是关于FreeMarker 复杂集合的主要内容,如果未能解决你的问题,请参考以下文章