FreeMarker Complex Collection

谁说胖子不能爱 提交于 2019-12-11 13:53:35

问题


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>

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>

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?


回答1:


Update:

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.

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.

Old answer (outdated):

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:

  • With BeansWrapper with its default settings Map keys mix with the method names, with method names having priority. Surely you can safely use myMap.get(key) to get around that, but myMap.get('foo') is just horrible compared to myMap.foo, which will only work as far you have no method called foo. Also ?keys will return a mixture of real keys and method names with it... it's a mess.

  • 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.

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...)

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 }.)



来源:https://stackoverflow.com/questions/18449159/freemarker-complex-collection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!