FreeMarker综合收藏 [英] FreeMarker Complex Collection
问题描述
在FreeMarker页面中,我有一个Map<Long, List<Map<String, Object>>> typeAndKno
的HashMap,我得到的地图内容如下:
I've got a HashMap as Map<Long, List<Map<String, Object>>> typeAndKno
, in the FreeMarker page, I get the content of this map like this:
<#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之后,该代码将失败.我终于将代码更改为此:
This code works fine in Struts2, but after moved to Spring MVC, the code fails. I finally changed the code to this:
<#list typeAndKno?keys as typeId>
${typeAndKno[typeId]?first['TYPE_NAME']}
<#list typeAndKno[typeId?string] as kno>
${kno['KNOWLEDGE_ID']}
</#list>
</#list>
这两段代码有什么区别?有没有一种方法可以使第一部分代码在Spring MVC中工作?
What's the difference between these two pieces of code? Is there a way to make the first piece of code work in Spring MVC?
推荐答案
更新:
从2.3.22版开始,有一个更简单且无中断的解决方案:配置FreeMarker以便?api
起作用,然后您可以使用Map
的Java API,其中键不是String
-s.请参见此FAQ条目或
As of 2.3.22 there's a much easier and non-disruptive solution for this: configure FreeMarker so that ?api
works, and then you can use the Java API of Map
where the keys aren't String
-s. See this FAQ entry or this answer for more details.
现在强烈反对Strut的FreeMarker设置.当然,那时候,当他们这样做的时候,那也许是最合理的解决方法,但这已经有一段时间了,尤其是从2.3.22开始.
And Strut's FreeMarker setup is something that's strongly discouraged now. Of course, back then, when they did that, that was maybe the most reasonable workaround, but it isn't anymore for a while, and especially not since 2.3.22.
旧答案(过时):
从模板查看Java对象的方式取决于所使用的ObjectWrapper
,这是FreeMarker配置设置.根据您的示例,Struts使用BeansWrapper
及其默认设置,而Spring可能使用DefaultObjectWrapper
.这样就造成了差异.我不建议使用任何一种,因为:
The way you see Java objects from templates depends on the ObjectWrapper
used, which is a FreeMarker configuration setting. Based on your example, Struts uses a BeansWrapper
with its default settings, while Spring possibly uses the DefaultObjectWrapper
. So that causes the differences. I wouldn't recommend using either, because:
-
具有
BeansWrapper
的默认设置Map
键与方法名称混合,其中方法名称优先.当然,您可以安全地使用myMap.get(key)
来解决此问题,但是与myMap.foo
相比,myMap.get('foo')
简直太可怕了,仅当您没有称为foo
的方法时,它才有效.同样,?keys
会返回包含实键和方法名的混合信息……这是一团糟.
With
BeansWrapper
with its default settingsMap
keys mix with the method names, with method names having priority. Surely you can safely usemyMap.get(key)
to get around that, butmyMap.get('foo')
is just horrible compared tomyMap.foo
, which will only work as far you have no method calledfoo
. Also?keys
will return a mixture of real keys and method names with it... it's a mess.
使用DefaultObjectWrapper
您可以安全地编写myMap.foo
,但是您将无法获得带有非字符串键的实体,因为myMap[key]
仅支持字符串,而您没有
With DefaultObjectWrapper
you can safely write myMap.foo
, but you won't be able to get entities with non-string keys, because myMap[key]
only support strings, and you don't have myMag.get(key)
anymore.
所以我通常在FreeMarker上使用的是bw = new BeansWrapper(); bw.setSimpleMapWrapper(true)
.这样,Map
-s的方法就不可见,就像DefaultObjectWrapper
一样,因此您可以安全地使用myMap.foo
和myMap[key]
.但是,如果很少需要使用非字符串键来获取内容,则可以使用myMap(nonStringKey)
(是的,使用()
而不是[]
).这最后一个不适用于DefaultObjectWrapper
. (希望FreeMarker 2.4可以解决非字符串键的问题,但这并不是很快就会消失的.)
So what I have usually used with FreeMarker was a bw = new BeansWrapper(); bw.setSimpleMapWrapper(true)
. With this, the methods of Map
-s are not visible, just like with DefaultObjectWrapper
, so you can use myMap.foo
and myMap[key]
safely. But if rarely you need to get something with a non-string key, you can use myMap(nonStringKey)
(yes, with ()
instead of []
). This last doesn't work with DefaultObjectWrapper
. (Hopefully FreeMarker 2.4 will solve this mess with non-string keys nonsense, but it's not like it will be out anytime soon...)
因此,下一个问题是如何使用Spring设置对象包装.我不在家.据我所知,您有一个FreeMarkerConfigurer
bean,它具有一个freemarkerSettings
属性,该属性是一个Properties
对象,最终将其传递给FreeMarker基于Properties
的配置API.因此,您应该能够添加一个object_wrapper
属性,该属性引用要使用的ObjectWrapper
的类名(或者可能只是beans
来使用默认的BeansWrapper
实例,就像Struts所做的那样).问题是,基于属性的API相当有限,因此您不能同时在其中创建和配置(调用setSimpleMapWrapper
)一个BeansWrapper
.您当然可以在spring配置文件中执行此操作,但是除非您将 whole freemarker.template.Configuration
对象创建为bean,否则我看不到将其注入FreeMarkerConfigurer
的方法.使用它的configuration
属性将其注入FreeMarkerConfigurer
中.因此,最简单的解决方法可能是扩展BeansWrapper
以覆盖默认的simpleMapWrapper
,然后通过object_wrapper
引用该扩展类的类. (以防万一以后有人阅读,FreeMarker 2.3.21可能会扩展属性配置API,以便您可以将object_wrapper
设置为BeansWrapper() { simpleMapWrapper = true }
.)
So the next question is how to set the object wrapper with Spring. I'm not at home there. As far as I see, you have a FreeMarkerConfigurer
bean which has a freemarkerSettings
property, which is a Properties
object that's eventually is passed to FreeMarker's Properties
-based configuration API. So there you should be able add an object_wrapper
property that refers to the class name of the ObjectWrapper
to use (or it could just be beans
to use the default BeansWrapper
instance like maybe Struts does). Problem is, the property-based API is rather limited, and so you can't both create and configure (call setSimpleMapWrapper
) a BeansWrapper
there. You could do that in the spring configuration file of course, but I don't see a way to inject that into the FreeMarkerConfigurer
, unless you create the whole freemarker.template.Configuration
object as a bean, and inject that into the FreeMarkerConfigurer
with the configuration
property of it. So maybe the easiest workaround is extending BeansWrapper
to override the default of simpleMapWrapper
, and then referring to the class of that extending class via object_wrapper
. (Just in case somebody reads this later, it's probable that FreeMarker 2.3.21 will extended the properties configuration API so that you can just set object_wrapper
to BeansWrapper() { simpleMapWrapper = true }
.)
这篇关于FreeMarker综合收藏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!