fbo*_*kov 8 java json jackson kotlin
我有一个 Kotlin 密封类 -Pet和两个子类 -Dog和Cat. 我的应用程序需要传输以 JSON 序列化的宠物集合。为了区分子类,我使用 Jackson@JsonTypeInfo和@JsonSubTypes注释。清单如下:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(
JsonSubTypes.Type(value = Dog::class, name = "dog"),
JsonSubTypes.Type(value = Cat::class, name = "cat")
)
sealed class Pet { abstract val name: String }
data class Dog(override val name: String): Pet()
data class Cat(override val name: String): Pet()
Run Code Online (Sandbox Code Playgroud)
单个实例已正确序列化和反序列化:
@Test
fun `serialize dog`() {
val dog = Dog("Kevin")
val dogJson = objectMapper.writeValueAsString(dog)
JsonAssert.assertEquals(dogJson, """{"type":"dog","name":"Kevin"}""")
val newDog = objectMapper.readValue<Dog>(dogJson)
}
Run Code Online (Sandbox Code Playgroud)
当对 pets 集合进行序列化和反序列化时,就会出现问题:
@Test
fun `serialize dog and cat`() {
val pets: Set<Pet> = setOf(Dog("Kevin"), Cat("Marta"))
val petsJson = objectMapper.writeValueAsString(pets)
JsonAssert.assertEquals(petsJson, """[{"name":"Kevin"},{"name":"Marta"}]""")
val newPets = objectMapper.readValue<Set<Pet>>(petsJson)
}
Run Code Online (Sandbox Code Playgroud)
Jackson 在序列化期间吞咽了类型属性,因此objectMapper无法readValue:
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class s3.moria.workflows.common.model.Pet]: missing type id property 'type'
at [Source: (String)"[{"name":"Kevin"},{"name":"Marta"}]"; line: 1, column: 17] (through reference chain: java.util.HashSet[0])
Run Code Online (Sandbox Code Playgroud)
有什么想法如何解决这个问题吗?或者解决方法?
杰克逊版本:2.9.0
这实际上不是一个错误,而是一个功能。对于具有泛型的集合,Jackson 将忽略您的子类型注释。这里有一个关于它的讨论:
https://github.com/FasterXML/jackson-databind/issues/1816
以下两个“修复”对我有用,并且比上面的答案需要更少的设置(我认为我们可能使用不同的杰克逊版本,但我无法让杰克逊使用子类的非默认构造函数,所以我用lateinit重写了子类定义)
克服这个问题的一种方法是:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(
JsonSubTypes.Type(value = Dog1::class, name = "dog"),
JsonSubTypes.Type(value = Cat1::class, name = "cat")
)
sealed class Pet1 {
abstract val name: String
}
class Dog1 : Pet1() {
override lateinit var name: String
}
class Cat1 : Pet1() {
override lateinit var name: String
}
Run Code Online (Sandbox Code Playgroud)
这些测试通过(同样 JSONAssert 对我来说似乎是不同的方法签名)
package com.example.demo
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import org.junit.jupiter.api.Test
import org.skyscreamer.jsonassert.JSONAssert
internal class PetTest1 {
private var objectMapper = ObjectMapper()
@Test
fun `serialize dog`() {
val dog = Dog1()
dog.name = "Kevin"
val dogJson = objectMapper.writeValueAsString(dog)
JSONAssert.assertEquals(dogJson, """{"type":"dog","name":"Kevin"}""", true)
val newDog = objectMapper.readValue<Dog1>(dogJson)
}
@Test
fun `serialize dog and cat with mapper`() {
val dog = Dog1()
dog.name = "Kevin"
val cat = Cat1()
cat.name = "Marta"
val pets: Set<Pet1> = setOf(dog, cat)
val petCollectionType = objectMapper.typeFactory
.constructCollectionType(Set::class.java, Pet1::class.java)
val petsJson = objectMapper.writer().forType(petCollectionType).writeValueAsString(pets)
JSONAssert.assertEquals(
petsJson, """[{"type":"dog","name":"Kevin"},{"type":"cat","name":"Marta"}]""", true
)
val newPets = objectMapper.readValue<Set<Pet1>>(petsJson)
}
}
Run Code Online (Sandbox Code Playgroud)
您还可以使用此方法: 无需自定义序列化器/反序列化器的解决方法
你的代码看起来像:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY)
@JsonSubTypes(
JsonSubTypes.Type(value = Dog::class, name = "dog"),
JsonSubTypes.Type(value = Cat::class, name = "cat")
)
sealed class Pet {
abstract val jacksonMarker: String
@JsonProperty("@type")
get
abstract val name: String
}
class Dog : Pet() {
override val jacksonMarker: String
get() = "dog"
override lateinit var name: String
}
class Cat : Pet() {
override val jacksonMarker: String
get() = "cat"
override lateinit var name: String
}
Run Code Online (Sandbox Code Playgroud)
以下测试通过
internal class PetTest {
private var objectMapper = ObjectMapper()
@Test
fun `serialize dog`() {
val dog = Dog()
dog.name = "Kevin"
val dogJson = objectMapper.writeValueAsString(dog)
JSONAssert.assertEquals(dogJson, """{"@type":"dog","name":"Kevin"}""", true)
val newDog = objectMapper.readValue<Dog>(dogJson)
}
@Test
fun `serialize dog and cat`() {
val dog = Dog()
dog.name = "Kevin"
val cat = Cat()
cat.name = "Marta"
val pets: Set<Pet> = setOf(dog, cat)
val petsJson = objectMapper.writeValueAsString(pets)
JSONAssert.assertEquals(
petsJson, """[{"@type":"dog","name":"Kevin"},{"@type":"cat","name":"Marta"}]""", true)
val newPets = objectMapper.readValue<Set<Pet>>(petsJson)
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7583 次 |
| 最近记录: |