使用Gson序列化含集合的Scala类对象未达预期,求解决
解决Gson序列化Scala集合(如Map)的问题
你遇到的问题太典型了——Gson是为Java生态设计的序列化库,它根本不原生理解Scala的集合类型(比如你代码里的Map)和case class的底层实现,直接序列化的话,输出的会是Scala集合内部的私有字段,而不是我们预期的标准JSON键值对结构。
下面给你几个实用的解决方案,按从简单到通用的顺序排列:
方案1:手动转成Java集合(快速临时解决)
最简单的办法就是在序列化前,把Scala集合转换成对应的Java集合,Gson对Java集合的支持是完美的:
import com.google.gson.Gson import scala.jdk.CollectionConverters._ object Hello extends App { case class FilterJson(version: Int, maps: Map[String, String], gameModes: Map[String, String]) def anyRefToJsonString(scalaValue: AnyRef): String = { val gson = new Gson // 针对你的FilterJson类型,把里面的Scala Map转成Java Map val adaptedValue = scalaValue match { case fj: FilterJson => fj.copy( maps = fj.maps.asJava, gameModes = fj.gameModes.asJava ) // 如果还有其他带Scala集合的类,可以在这里继续加匹配逻辑 case other => other } gson.toJson(adaptedValue) } // 测试一下 val testFilter = FilterJson(1, Map("key1" -> "val1"), Map("mode1" -> "classic")) println(anyRefToJsonString(testFilter)) }
运行后就能得到你想要的标准JSON:
{"version":1,"maps":{"key1":"val1"},"gameModes":{"mode1":"classic"}}
方案2:自定义Gson TypeAdapter(通用适配)
如果你需要处理很多不同的Scala类型,手动转换太麻烦,可以给Scala Map写一个自定义的TypeAdapter,让Gson学会怎么正确处理它:
import com.google.gson._ import scala.jdk.CollectionConverters._ // 为Scala的String-String Map写一个适配器 class ScalaMapTypeAdapter extends TypeAdapter[Map[String, String]] { override def write(out: JsonWriter, value: Map[String, String]): Unit = { out.beginObject() value.foreach { case (k, v) => out.name(k).value(v) } out.endObject() } override def read(in: JsonReader): Map[String, String] = { val tempMap = scala.collection.mutable.Map.empty[String, String] in.beginObject() while (in.hasNext) { tempMap += (in.nextName() -> in.nextString()) } in.endObject() tempMap.toMap } } object Hello extends App { case class FilterJson(version: Int, maps: Map[String, String], gameModes: Map[String, String]) def anyRefToJsonString(scalaValue: AnyRef): String = { val gson = new GsonBuilder() // 注册我们写的适配器 .registerTypeAdapter(classOf[Map[String, String]], new ScalaMapTypeAdapter()) .create() gson.toJson(scalaValue) } // 测试示例 val testFilter = FilterJson(1, Map("key1" -> "val1"), Map("mode1" -> "classic")) println(anyRefToJsonString(testFilter)) }
这个方法扩展性很强,如果你还有Scala List、Set这类集合需要处理,都可以写对应的TypeAdapter注册进去。
方案3:换用Scala原生JSON库(长期推荐)
如果项目允许,我真心建议你换成专门为Scala设计的JSON库,比如Circe、Play JSON或者uPickle——这些库天生就支持Scala的所有类型(case class、集合、Option、Tuple等等),完全不需要额外适配,代码也更简洁。
举个Circe的例子:
import io.circe._ import io.circe.generic.semiauto._ import io.circe.syntax._ object Hello extends App { case class FilterJson(version: Int, maps: Map[String, String], gameModes: Map[String, String]) // 自动生成编码器,一行搞定 implicit val filterJsonEncoder: Encoder[FilterJson] = deriveEncoder[FilterJson] val testFilter = FilterJson(1, Map("key1" -> "val1"), Map("mode1" -> "classic")) val jsonString = testFilter.asJson.noSpaces println(jsonString) }
输出的结果同样是标准JSON,而且后续不管你加什么Scala类型的字段,都能轻松序列化,省心太多了。
内容的提问来源于stack exchange,提问作者user3651946
相关产品推荐
相关产品推荐

