父类上注明,在需要多态属性上使用jackson注解。多层父类如果使用到,如 new TypeReference[List[...]] {}
。每层都需要添加注解。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "clz", visible = true)
@JsonSubTypes(value = Array( ... )
原理就:反序列化时候使用类中已有的clz
属性值。判定类型并反序列化。
举个例子:
定义类:
import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo}
import com.fasterxml.jackson.annotation.JsonSubTypes.Type
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "clz", visible = true)
@JsonSubTypes(value = Array(
new Type(value = classOf[Triangle], name = "Triangle")
, new Type(value = classOf[Rectangle], name = "Rectangle")
, new Type(value = classOf[Circle], name = "Circle")
))
trait Shape {
def name: String // 形状名称
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "clz", visible = true)
@JsonSubTypes(value = Array(
new Type(value = classOf[Triangle], name = "Triangle")
, new Type(value = classOf[Rectangle], name = "Rectangle")
))
trait Polygon extends Shape {
def edges: Int
// 多边形
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "clz", visible = true)
@JsonSubTypes(value = Array(
new Type(value = classOf[Circle], name = "Circle")
))
trait curve extends Shape {
// 曲线
}
// 三角形
case class Triangle(var name: String
, var edges: Int
) extends Polygon with Product with Serializable {
val clz: String = "Triangle"
}
// 长方形
case class Rectangle(var name: String
, var edges: Int
) extends Polygon with Product with Serializable {
val clz: String = "Rectangle"
}
// 圆形
case class Circle(var name: String
, var edges: Int
, var diameter: Int
) extends curve with Product with Serializable {
val clz: String = "Circle"
}
测试
import com.fasterxml.jackson.core.`type`.TypeReference
import com.fasterxml.jackson.databind.{DeserializationFeature, ObjectMapper}
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import org.scalatest.funsuite.AnyFunSuite
class ShapeTest extends AnyFunSuite {
def getMapper: ObjectMapper = {
var mapper: ObjectMapper = JsonMapper.builder()
.addModule(DefaultScalaModule)
.build()
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
mapper
}
val mapper = getMapper
test("serde") {
val t1 = Triangle("right triangle", 3)
val r1 = Rectangle("square", 4)
val c1 = Circle("circle", 0, 10)
val ls1: List[Shape] = List(t1, c1)
val str1 = mapper.writeValueAsString(ls1)
println(str1) // [{"name":"right triangle","edges":3,"clz":"Triangle"},{"name":"circle","edges":0,"diameter":10,"clz":"Circle"}]
val ls2 = mapper.readValue(str1, new TypeReference[List[Shape]] {})
println(ls2) // List(Triangle(right triangle,3), Circle(circle,0,10))
val ls3: List[Polygon] = List(t1, r1)
val str2 = mapper.writeValueAsString(ls3)
println(str2) // [{"name":"right triangle","edges":3,"clz":"Triangle"},{"name":"square","edges":4,"clz":"Rectangle"}]
val ls4 = mapper.readValue(str2, new TypeReference[List[Polygon]] {})
println(ls4) // List(Triangle(right triangle,3), Rectangle(square,4))
}
}
结果:
(1)多层集成也没问题。最终都会反序列化为case class定义的clz类型。
(2)自定义的clz属性保证类型不会被更改。