是否可以在自定义序列化程序中序列化为已知的默认格式?

Is it possible to serialize as known default format in custom serializer?

我必须反序列化 JSON 响应,该响应可以将其中一个字段设置为不同的对象(只有一个公共字段)。现实生活中的模型相当复杂,但例如我们可以用两个案例 类 扩展密封特征来表示它:

sealed trait Item {
  val itemType: String
}

case class FirstItem(
  itemType: String = "FirstItem",
  firstProperties: SomeComplexType
) extends Item

case class SecondItem(
  itemType: String = "SecondItem",
  secondProperties: SomeOtherComplexType,
  secondName: String,
  secondSize: Int
) extends Item

由于 Json4s 不知道如何处理我编写自定义序列化程序的对象:

object ItemSerializer extends CustomSerializer[Item](_ => ({
  case i: JObject => 
    implicit val formats: Formats = DefaultFormats
    (i \ "itemType").extract[String] match {
      case "FirstType" => i.extract[FirstItem]
      case "SecondItem" => i.extract[SecondItem]
    }
}, {
  case x: Item => x match {
    case f: FirstItem => JObject() //TODO
    case s: SecondItem => JObject() //TODO
  }
}))

第一部分 - 反序列化并不完美,因为它强烈依赖于类型字段,但它可以满足我的需要。问题是第二部分——序列化。在示例中,我发现人们通常会逐步记下每个字段,但通常,他们会序列化一些简单的对象。在我的例子中,这个对象有多个级别,总共有超过 60-80 个字段,因此它会导致相当混乱且难以阅读的代码。所以我想知道是否有更好的方法来做到这一点,因为 FirstItemSecondItem 都可以仅使用 DefaultFormats 进行反序列化。有什么方法可以告诉 Json4s,如果对象匹配给定的类型,则应该使用默认格式对其进行序列化?

我深入研究了各种示例,结果证明这是可行的,而且非常简单。有一个 org.json4s.Extraction.decompose() 方法可以处理所有事情,例如:

object ItemSerializer extends CustomSerializer[Item](_ => ({
  case i: JObject => 
    implicit val formats: Formats = DefaultFormats
    (i \ "itemType").extract[String] match {
      case "FirstType" => i.extract[FirstItem]
      case "SecondItem" => i.extract[SecondItem]
    }
}, {
  case x: Item => 
    implicit val formats: Formats = DefaultFormats
    x match {
      case f: FirstItem => Extraction.decompose(f)
      case s: SecondItem => Extraction.decompose(s)
    }
}))

但是,我对所描述问题的解决方案是错误的。我不需要指定额外的序列化程序。我需要的只是 trait 的伴随对象,它包含每种数据格式的构造函数,Json4s 可以完美地处理所有事情,例如:

object Item{
  def apply(
    itemType: String, 
    firstProperties: SomeComplexType
  ): Item = FirstItem(itemType, firstProperties)

  def apply(
    itemType: String, 
    secondProperties: SomeOtherComplexType, 
    secondName: String, secondSize: Int
  ): Item = SecondItem(itemType, secondProperties, secondName, secondSize)
}