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一样方便 不同之处在于 FreeMarker object_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.foomyMap[key]。但是,如果您很少需要使用非字符串键获取某些内容,则可以使用myMap(nonStringKey)(是的,使用() 而不是[])。最后一个不适用于DefaultObjectWrapper。 (希望 FreeMarker 2.4 能解决这个乱七八糟的非字符串键问题,但它不会很快就出来......)

那么接下来的问题是如何用Spring设置对象包装器。我不在家。据我所知,你有一个 FreeMarkerConfigurer bean,它有一个 freemarkerSettings 属性,这是一个 Properties 对象,最终传递给 FreeMarker 的基于 Properties 的配置 API。因此,您应该可以添加一个 object_wrapper 属性,该属性引用要使用的 ObjectWrapper 的类名(或者它可能只是 beans 以使用默认的 BeansWrapper 实例,就像 Struts 一样)。问题是,基于属性的 API 相当有限,因此您不能同时创建和配置(调用 setSimpleMapWrapperBeansWrapper。当然,您可以在 spring 配置文件中执行此操作,但我看不到将其注入 FreeMarkerConfigurer 的方法,除非您将 whole freemarker.template.Configuration 对象创建为 bean,并且将其注入到 FreeMarkerConfigurer 中,并带有 configuration 属性。所以也许最简单的解决方法是扩展BeansWrapper 以覆盖默认的simpleMapWrapper,然后通过object_wrapper 引用该扩展类的类。 (以防有人稍后阅读此内容,FreeMarker 2.3.21 可能会扩展属性配置 API,以便您可以将 object_wrapper 设置为 BeansWrapper() simpleMapWrapper = true 。)

【讨论】:

以上是关于FreeMarker 复杂集合的主要内容,如果未能解决你的问题,请参考以下文章

freemarker可以用来输出自定义集合吗?

FreeMarker 集合遍历

freemarker list集合去重,实现hashset

利用freemarker导出页面格式复杂的excel

利用freemarker导出页面格式复杂的excel

Spring Boot:Spring Boot整合FreeMarker