我必须反序列化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个字段,因此将导致混乱且难以阅读代码。所以我想知道是否有更好的方法,因为FirstItem
和SecondItem
都可以仅使用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)
}