Playframework中Scala模板嵌套Map转JSON字符串的方法咨询
我之前在Play项目里处理Java集合转JSON时也踩过类似的坑,结合你的情况,给你几个靠谱的解决方案:
先搞对导入的JSON库
你提到之前导入了错误的@import play.api.libs.json._,这里要分两种情况:
如果你的项目是Java Play:应该导入Play为Java专门提供的JSON工具:
@import play.libs.Json这个库对Java集合(比如
java.util.Map、Integer这类类型)支持非常好,直接用Json.toJson()就能处理你的嵌套Map。如果是Scala Play项目:
play.api.libs.json._是正确的导入,但它默认只支持Scala原生集合,对Java集合不友好,所以需要先把Java Map转换成Scala集合。
方案1:Java Play项目直接用Java JSON库
如果是Java Play,直接替换导入后,一行代码就能解决:
@Json.stringify(Json.toJson(moduleId2DecileMap))
Play的Java JSON工具会自动处理java.util.Map<Integer, Map<Integer, Double>>这种嵌套结构,不需要额外转换。
方案2:Scala Play项目转换为Scala集合
因为Scala的JSON库只认Scala的Map和Int,所以我们可以用Scala的集合转换工具把Java Map转成Scala类型:
@import scala.jdk.CollectionConverters._ @import play.api.libs.json._ @defining(moduleId2DecileMap.asScala.map { case (javaKey, javaInnerMap) => javaKey.toInt -> javaInnerMap.asScala.map { case (innerKey, value) => innerKey.toInt -> value } }) { scalaNestedMap => @Json.stringify(Json.toJson(scalaNestedMap)) }
这里用asScala把Java Map转成Scala的Map,再把Integer转成Scala的Int,这样Json.toJson()就能正常序列化了。
方案3:自定义JSON序列化规则(进阶)
如果不想每次都手动转换集合,可以在Scala代码里定义隐式的Writes来支持Java嵌套Map:
// 在你的Scala控制器或工具类里定义 import play.api.libs.json._ import scala.jdk.CollectionConverters._ // 处理内层的Java Map<Integer, Double> implicit val javaDoubleMapWrites: Writes[java.util.Map[Integer, Double]] = Writes { map => Json.obj(map.asScala.map { case (k, v) => k.toString -> v }: _*) } // 处理外层的Java Map<Integer, Map<Integer, Double>> implicit val javaNestedMapWrites: Writes[java.util.Map[Integer, java.util.Map[Integer, Double]]] = Writes { map => Json.obj(map.asScala.map { case (k, v) => k.toString -> Json.toJson(v) }: _*) }
然后在模板里导入这个隐式值,就可以直接用Json.toJson(moduleId2DecileMap)了。
为什么之前会失败?
核心原因是Play的Scala JSON库play.api.libs.json默认没有提供java.util.Map的序列化规则,而你用的是Java泛型的Map,所以系统报错无法处理。要么转成Scala集合,要么用Java版的JSON库,就能解决问题。
内容的提问来源于stack exchange,提问作者bharal

