为Freemarker的Java List<Map>变量重写toString()遇遍历问题求助
问题分析与解决方案
你的问题出在自定义包装类的实现逻辑上:你继承DefaultObjectWrapper包装后的对象被FreeMarker识别成了StringModel(因为它优先支持字符串输出),而StringModel同时实现了TemplateHashModel接口,所以FreeMarker会把它当作哈希结构而非序列(List)处理,遍历的时候就要求你指定键值对变量。
要同时满足两种需求,你不需要继承DefaultObjectWrapper,而是让自定义包装类同时实现TemplateScalarModel和TemplateSequenceModel,这样FreeMarker既能识别它是可遍历的序列,又能直接输出字符串。
具体实现步骤
自定义包装类,持有
List<Map>的实例,同时实现两个核心接口:TemplateScalarModel:负责处理${myListOfMapsVar}的字符串输出,在getAsString()方法中返回列表的toString()结果。TemplateSequenceModel:负责支持<#list>遍历,实现size()和get(int index)方法,注意返回的Map对象需要用默认包装器处理,才能在模板中通过item.mapKeyName访问属性。
代码示例:
import freemarker.template.*; import java.util.List; import java.util.Map; public class ListOfMapsWrapper implements TemplateScalarModel, TemplateSequenceModel { private final List<Map<String, Object>> targetList; private final ObjectWrapper defaultWrapper; public ListOfMapsWrapper(List<Map<String, Object>> targetList, ObjectWrapper defaultWrapper) { this.targetList = targetList; // 传入默认包装器,用来处理List中的Map对象 this.defaultWrapper = defaultWrapper; } // 处理${}输出字符串 @Override public String getAsString() throws TemplateModelException { return targetList.toString(); } // 处理list遍历的长度 @Override public int size() throws TemplateModelException { return targetList.size(); } // 获取遍历的每个元素,并用默认包装器包装Map @Override public TemplateModel get(int index) throws TemplateModelException { if (index < 0 || index >= targetList.size()) { return null; } Map<String, Object> map = targetList.get(index); return defaultWrapper.wrap(map); } }
- 使用方式:
在把List<Map>放入模板上下文时,用这个自定义包装类包装:
// 获取默认包装器(不能修改全局配置的话,直接用FreeMarker的默认实例) ObjectWrapper defaultWrapper = DefaultObjectWrapper.getDefaultInstance(Configuration.VERSION_2_3_32); List<Map<String, Object>> myList = ...; // 你的List<Map>数据 templateModel.put("myListOfMapsVar", new ListOfMapsWrapper(myList, defaultWrapper));
这样处理后,模板中既可以用${myListOfMapsVar}输出字符串,也可以用<#list myListOfMapsVar as item>遍历,直接通过item.mapKeyName访问Map中的属性。
内容的提问来源于stack exchange,提问作者Mark Roy
相关产品推荐
相关产品推荐

