【问题标题】:Serializing/Deserializing case objects with Gson in Scala在 Scala 中使用 Gson 序列化/反序列化案例对象
【发布时间】:2015-08-10 19:59:37
【问题描述】:

我正在使用 Gson 对对象进行序列化和反序列化,并将结果保存在 Redis 中。即对象被序列化为json字符串然后放入Redis中,当对象被检索时,它是字符串然后我使用Gson.fromjson(str,className)反序列化为对象。

我是 Scala 的初学者,所以我认为我的用法不正确。

我有以下课程:

case class Status(id: String, state: State)

其中状态如下:

sealed trait State {}

case object COMPLETED_SUCCESSFULLY extends State {}

case object FINISHED_POLLING extends State {}

case object CURRENTLY_DOWNLOADING extends State {}

case object FINISHED_DOWNLOADING extends State {}

case object CURRENTLY_UPLOADING extends State {}

case object FINISHED_UPLOADING extends State {}

我想将Status序列化成一个json字符串,然后反序列化成一个对象。

但是,当我使用 Gson 序列化 Status 时,我得到:

"{\"id\":\"foo\",\"state\":{}}"

这是为什么呢?

例如:

val Status = new Status("foo", COMPLETED_SUCCESSFULLY)

我希望序列化的输出是

"{\"id\":\"foo\",\"state\":\"COMPLETED_SUCCESSFULLY\"}"

【问题讨论】:

  • 你期望什么输出?
  • @ka4eli 更新了问题。我只是希望能够使用 gson 进行序列化和反序列化。

标签: scala gson


【解决方案1】:

默认情况下,案例对象由 Gson 序列化为空 json 对象:{}。您必须编写自定义序列化程序才能获得预期的行为:

object StateSerializer extends JsonSerializer[State] {
  override def serialize(t1: State, t2: Type, jsonSerializationContext: JsonSerializationContext): JsonElement = {
    val res = new JsonObject()
    res.add("name", new JsonPrimitive(t1.toString))
    res
  }
}


val gson = new GsonBuilder().registerTypeHierarchyAdapter(classOf[State], StateSerializer)
.registerTypeHierarchyAdapter(classOf[State], StateDeserializer).setPrettyPrinting().create()

println(gson.toJson(COMPLETED_SUCCESSFULLY))  

将打印:

{
  "name": "COMPLETED_SUCCESSFULLY"
}

另外,如果你想将 json 转换为 case 对象,你必须实现 JsonDeserializer:

object StateDeserializer extends JsonDeserializer[State] {
  override def deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): State = {
    val res = json match {
      case o: JsonObject if o.has("name") && o.entrySet().size() == 1 =>
        val name = o.get("name").getAsString
        name match {
          case "FINISHED_POLLING" => FINISHED_POLLING
          case "FINISHED_DOWNLOADING" => FINISHED_DOWNLOADING
          case "FINISHED_UPLOADING" => FINISHED_UPLOADING
          case "CURRENTLY_DOWNLOADING" => CURRENTLY_DOWNLOADING
          case "CURRENTLY_UPLOADING" => CURRENTLY_UPLOADING
          case "COMPLETED_SUCCESSFULLY" => COMPLETED_SUCCESSFULLY
          case _ => null
        }
      case _ => null
    }

    Option(res).getOrElse(throw new JsonParseException(s"$json can't be parsed to State"))
  }
}

println(gson.fromJson("{\"name\": \"COMPLETED_SUCCESSFULLY\"}", classOf[State])) 

将打印:

COMPLETED_SUCCESSFULLY

【讨论】:

  • 这是否意味着我必须为我拥有的每个枚举编写序列化程序?
  • 另外,为了反序列化,我不能只做 gson.fromJson(String, classOf[Status])?
  • 更新了答案。现在你有一个所有状态的序列化器。是的,你不能。因为Gson不知道这个{ "name": "COMPLETED_SUCCESSFULLY" }对应COMPLETED_SUCCESSFULLY
  • 生活很艰难...根据我上面发布的信息,有没有办法做到这一点?将值对象转换为放入redis的字符串,然后从redis中获取并解析/反序列化为值对象?我不完全确定密封特性是否是正确的方法......
  • 使用反序列化器更新了答案。现在你可以一切了)