我有一个Kotlin密封类--Pet
和两个子类--Dog
和Cat
。我的应用程序需要传输用JSON序列化的pets集合。为了区分子类,我使用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()
正确序列化和反序列化单个实例:
@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)
}
当宠物的集合被序列化和反序列化时,问题就出现了:
@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)
}
Jackson在序列化过程中吞并了type属性,因此,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])
有没有办法解决这个问题?还是变通办法?
Jackson版本:2.9.0
您应该首先在JsonTypeInfo
中进行更改,您需要将可见性设置为true,然后类型属性在反序列化程序中可用。
然后需要实现PetDeserializer
这里有一个例子:宠物。kt
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonProcessingException
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import java.io.IOException
import kotlin.jvm.Throws
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type",visible=true)
@JsonSubTypes(
JsonSubTypes.Type(value = Dog::class, name = "dog"),
JsonSubTypes.Type(value = Cat::class, name = "cat")
)
@JsonDeserialize(using = PetDeserializer::class)
sealed class Pet {
abstract val name: String
}
data class Dog(override val name: String) : Pet()
data class Cat(override val name: String) : Pet()
class PetDeserializer @JvmOverloads constructor(vc: Class<*>? = Pet::class.java) :
StdDeserializer<Pet?>(vc) {
@Throws(IOException::class, JsonProcessingException::class)
override fun deserialize(jp: JsonParser, ctxt: DeserializationContext): Pet {
val node = jp.codec
.readTree<JsonNode>(jp)
val itemName = node["name"]
.asText()
val type = node["type"]
.asText()
return when (type) {
"dog" -> Dog(itemName)
"cat" -> Cat(itemName)
else -> throw Error("unknown type")
}
}
companion object {
private const val serialVersionUID = 1883547683050039861L
}
}
还有PetTest.kt
import com.fasterxml.jackson.databind.ObjectMapper
import org.junit.Test
import org.skyscreamer.jsonassert.JSONAssert
class HelloTest {
val objectMapper = ObjectMapper()
@Test
fun `serialize dog`() {
val dog = Dog("Kevin")
val dogJson = objectMapper.writeValueAsString(dog)
JSONAssert.assertEquals("""{"type":"dog","name":"Kevin"}""", dogJson, false)
val dogType = objectMapper.typeFactory.constructType(Dog::class.java)
if (objectMapper.canDeserialize(dogType)) {
ObjectMapper().readValue<Dog>(dogJson, Dog::class.java)
} else {
throw Error("deserializer not loaded")
}
}
}
Maven依赖项:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<version>1.5.0</version>
</dependency>
这实际上不是一个bug,而是一个特性。对于具有泛型的集合,Jackson将忽略您的子类型注释。这里有一个关于它的讨论:
https://github.com/FasterXML/jackson-databind/issues/1816
下面的2个修复对我来说是有效的,并且比上面的答案需要更少的设置(我想我们可能使用不同的jackson版本,但是我不能让jackson为子类使用非默认构造函数,所以我用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
}
这些测试通过了(对我来说,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)
}
}
您还可以使用这种方法:不使用自定义序列化程序/反序列化程序的变通方法
您的代码如下所示:
@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
}
以下测试通过
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)
}
}
我有一个抽象类的具体对象。 我在抽象类和子类上使用注释,但JSON输出看起来不正确,而且我一直在反序列化上遇到异常。 我还在学习杰克逊,不幸的是,很多关于这个主题的教程已经过时了。 以下是底部有我的对象映射器的类: 抽象类: 混凝土等级: Objectmapper块: 输出JSON: 例外: 其他答案(例如多态类型的Jackson反序列化)建议使用方法,但根据我阅读的教程,这应该没有必要。 不知道
我想从Firestore获取会议并将其映射到以下模型中: 从Firestore获取文档,然后在iterable上调用< code>fromJson,但会引发一个异常: 当我进入生成的< code>meeting.g.dart时,就是这一行导致了错误 为了解决此问题,我尝试在模型中从 DateTime 更改为 Timestamp,但随后显示以下生成错误: 你能告诉我你如何解决这个问题吗?有没有另一种
问题内容: 我想将jackson json库用于通用方法,如下所示: … 现在的问题是,当我调用位于请求对象内的getMyObject()时,杰克逊将嵌套的自定义对象作为LinkedHashMap返回。有什么方法可以指定需要返回T对象吗?例如:如果我发送了类型为Customer的对象,则应该从该List?中返回Customer。 问题答案: 这是Java类型擦除的一个众所周知的问题:T只是一个类型
首先,我对处理HTTP请求和其他东西非常陌生。 我jave在JavaSpring中创建了一个API,我有一堆健身程序分配给一个用户,但是当我试图从Flutter中的JSON中获取对象时,会发生这个错误。我使用超文本传输协议和转换镖包。 列表不是类型映射的子类型 问题在于这一行: 这是getUsers方法: 以下是用户和健身计划课程:
我是新手。我正在开发一个测验应用程序,并拥有以下三个dart文件: 主要的飞奔 question.dart answer.dart 当我在android studio中的android上运行应用程序时,出现以下错误: ══╡ 小部件库捕获的异常╞═══════════════════════════════════════════ 生成MyApp时引发了以下类型的错误(脏,状态:_MyAppSta
在存储到本地数据库之前,将字符串转换为对象的正确方法是什么? 这是的输出: 我试图将其转换为CreatedBy对象 创造的 这里是我的本地表列 错误