You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:27:41