在FreeMarker模板中遍历Scala Map值失败的问题排查
FreeMarker遍历Scala Map值时抛出NonStringOrTemplateOutputException错误
错误信息
freemarker.core.NonStringOrTemplateOutputException: For "${...}" content: Expected a string or something automatically convertible to string (number, date or boolean), or "template output" , but this has evaluated to a method+sequence (wrapper: f.e.b.SimpleMethodModel): [info] ==> v [in template "test.ftl" at line 22, column 11]
相关代码
test.ftl模板代码
<ul> <#list params as k,v> <li>${v}</li> <#else> <p>Empty map</p> </#list> </ul>
Scala数据准备代码
def sampleHtmlTemplateWithMap(): util.Map[String, Any] = { val data = Map("param1" -> "artifact1", "param2" -> "artifact2") val root = new mutable.HashMap[String, Any]; root("params") = data root.asJava }
单元测试代码
val out = new StringWriter testTemplate.process(sampleHtmlTemplateWithMap, out)
尝试过的操作及结果
- 尝试遍历Map值(
<#list params?values as v>),抛出相同错误 - 遍历Map键(
<#list params?keys as k>)时无报错,但输出大量Scala集合类的方法名,非预期的Map键 - 单独通过键访问元素(
${params["param1"]})可行,返回结果为Some(artifact1) - 直接打印整个Map(
${params})正常,输出:Map(param1 -> artifact1, param2 -> artifact2)
问题原因及解决方案
问题根源
传入FreeMarker的params本质是Scala的Map实例,而非Java标准的java.util.Map。FreeMarker默认的对象包装器无法正确识别Scala集合的Map特性,导致遍历逻辑错误:将Map实例当成普通对象处理,遍历其方法/属性而非键值对,最终渲染值时将方法对象当成了要输出的内容,抛出类型转换异常。
解决方案
将Scala的Map显式转换为Java的java.util.Map,确保FreeMarker能正确识别为Map类型。修改Scala数据准备代码:
import scala.collection.JavaConverters._ def sampleHtmlTemplateWithMap(): util.Map[String, Any] = { val data = Map("param1" -> "artifact1", "param2" -> "artifact2") val root = new mutable.HashMap[String, Any]() // 将Scala Map转换为Java Map root("params") = data.asJava root.asJava }
修改后,FreeMarker就能正确解析params为Map类型,<#list params as k,v>和<#list params?values as v>都能正常遍历并输出值,单独访问键也会直接返回字符串值而非Some(...)。
内容的提问来源于stack exchange,提问作者user1452348
相关产品推荐
相关产品推荐

