我有两个案例类和一个特征,格式如下:
trait Parent
case class ChildClassOne(kind: String = "first_type", id: String) extends Parent
case class ChildClassTwo(kind: String = "second_type", id: String) extends Parent
另一个包含Parent
s列表的案例类:
case class ParentResponse(total: Int, results: List[Parent])
基本上,json响应可能有一个对象列表,可以是ChildClassOne
或ChildClassTwo
类型。
因为这个(我认为)我需要创建一个自定义序列化器:
class ParentSerializer extends CustomSerializer[Parent](format => ( {
case JObject(List(JField("kind", JString(kind)), JField("id", JString(id))))
if kind == "first_type" => ChildClassOne(kind, id)
case JObject(List(JField("kind", JString(kind)), JField("id", JString(id))))
if kind == "second_type" => ChildClassTwo(kind, id)
}, {
case _ => null
}))
这很好用。问题是这些对象可能会变得很大,我不想在自定义序列化程序中指定每个字段。我也没有以任何方式修改属性,并且使用自定义序列化程序只是为了根据kind
字段返回正确类型的案例类。
有没有办法避免在JObject
中指定每个字段,只是让非自定义序列化程序负责创建正确的案例类?例如
case JObject(List(JField("kind", JString(kind))))
if kind == "first_type" => read[ChildClassOne](format)
答案 0 :(得分:3)
您不需要自定义序列化程序,但需要一些自定义TypeHints来指定自定义“种类”字段与对象类之间的映射。
trait Parent
case class ChildClassOne(kind: String = "first_type", id: String) extends Parent
case class ChildClassTwo(kind: String = "second_type", id: String) extends Parent
case class ParentResponse(total: Int, results: List[Parent])
object MyTypeHints extends TypeHints {
// map class to kind and viceversa
val classToHint: Map[Class[_], String] = Map (
classOf[ChildClassOne] -> "first_type",
classOf[ChildClassTwo] -> "second_type"
)
val hintToClass = classToHint.map(_.swap)
override val hints: List[Class[_]] = List(classOf[ChildClassOne], classOf[ChildClassTwo])
override def classFor(hint: String): Option[Class[_]] = hintToClass.get(hint)
override def hintFor(clazz: Class[_]): String = classToHint(clazz)
}
implicit val formats = Serialization.formats(MyTypeHints).withTypeHintFieldName("kind")
val obj = ParentResponse(2, List(ChildClassOne(id = "one"), ChildClassTwo(id = "two")))
val serialized = Serialization.write(obj)
val deserializedFromString = Serialization.read[ParentResponse](
"""{"total":2,"results":[{"kind":"first_type","kind":"first_type","id":"one"},
{"kind":"second_type","kind":"second_type","id":"two"}]}""")
val deserializedFromSerialized = Serialization.read[ParentResponse](serialized)
assert(obj == deserializedFromString)
assert(obj == deserializedFromSerialized)
如果您不需要自定义类型提示字段,则可以使用默认字段。在readme
中搜索序列化多态列表答案 1 :(得分:0)
我设法最终使用Serializer来解决问题:
trait Parent
case class ChildClassOne(kind: String = "first_type", id: String) extends Parent
case class ChildClassTwo(kind: String = "second_type", id: String) extends Parent
case class ParentResponse(total: Int, results: List[Parent])
class ParentSerializer extends Serializer[Parent] {
private val ParentClass = classOf[Parent]
implicit val formats = DefaultFormats
def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Parent] = {
case (TypeInfo(ParentClass, _), json) => json match {
case JObject(JField("kind", JString(kind)) :: _) => kind match {
case "first_type" => json.extract[ChildClassOne]
case "second_type" => json.extract[ChildClassTwo]
}
case _ => throw new MappingException("Invalid kind")
}
}
def serialize(implicit format: Formats): PartialFunction[Any, JValue] = Map()
}
implicit val formats = DefaultFormats + new ParentSerializer