如何在Kotlin中使用Jackson的JsonIdentityInfo对象引用对列表进行美化?

时间:2019-01-10 16:14:02

标签: android kotlin jackson2

假设您从JSON格式的API调用中接收到对象列表,并且此对象引用了带JsonIdentityInfo注释的类。如果使用更多对象对列表进行resserialize,则如果JsonIdentityInfo带注释的对象在列表的添加元素中重复出现,您将获得2个JSON对象定义(当您尝试再次对其进行反序列化时,将导致InvalidDefinitionException)。看起来在反序列化过程中涉及了一些缓存,所以以前的值被重用了。是否可以避免这种行为?

我正在使用Kotlin开发Android,这意味着我使用Kotlin Jackson。

我编写了以下代码来说明正在发生的事情:

fun test_jsonIdentityInfo_reserialization(){
    val bar = Bar(1, "bar")
    val f1 = Foo("firstFoo", bar)
    val f2 = Foo("secondFoo", bar)
    val l1 = listOf(f1, f2)

    val firstListString = JsonMapper.serialize(l1) ?: ""
    println("First list serialized:")
    println(firstListString)
    println()
    val firstListDeserialized = JsonMapper.deserialize<Collection<Foo>>(firstListString)?.toList() ?: emptyList()
    println("First list desserialized:")
    println(firstListDeserialized)
    println()

    val f3 = Foo("thirdFoo", bar)
    val f4 = Foo("fortyFoo", bar)
    val l2 = ... // See bellow what I tried

    val secondListString = JsonMapper.serialize(l2) ?: ""
    println("Second list serialized:")
    println(secondListString)
    println()
    val secondListDeserialized = JsonMapper.deserialize<Collection<Foo>>(secondListString) ?: emptyList()
    println("Second list desserialized:")
    println(secondListDeserialized)
    println()
}

data class Foo(
@JsonProperty("name")
val name: String = "",
@JsonProperty("bar")
val bar: Bar)

@JsonIdentityInfo(property = "id", generator = ObjectIdGenerators.PropertyGenerator::class, scope = Bar::class)
data class Bar(
@JsonProperty("id")
val id: Int = -1,
@JsonProperty("name")
val name: String = "")

object JsonMapper {
    private val mapper: ObjectMapper by lazy {
        ObjectMapper().registerKotlinModule().apply{
            configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT)
        }
    }

    inline fun <reified T> reify(): TypeReference<T> =
        object : TypeReference<T>(){}

    fun <T> serialize(data: T, ref: TypeReference<T>): String? =
        mapper.writerFor(ref).writeValueAsString(data)

    inline fun <reified T> serialize(data: T): String? = 
        serialize(data, reify())

    fun <T> deserialize(data: String, type: TypeReference<T>): T? =
        mapper.readValue(data, type)

    inline fun <reified T> deserialize(data: String): T? = 
        deserialize(data, reify<T>())
}

我尝试了以下操作,更改了“ l2”值。

何时:

val l2 = listOf(f1, f2, f3, f4)

或:

val l2 = l1 + listOf(f3, f4)

退出:

First list serialized:
[{"name":"firstFoo","bar":{"id":1,"name":"bar"}},{"name":"secondFoo","bar":1}]

First list desserialized:
[Foo(name=firstFoo, bar=Bar(id=1, name=bar)), Foo(name=secondFoo, bar=Bar(id=1, name=bar))]

Second list serialized:
[{"name":"firstFoo","bar":{"id":1,"name":"bar"}},{"name":"secondFoo","bar":1},{"name":"thirdFoo","bar":1},{"name":"fortyFoo","bar":1}]

Second list desserialized:
[Foo(name=firstFoo, bar=Bar(id=1, name=bar)), Foo(name=secondFoo, bar=Bar(id=1, name=bar)), Foo(name=thirdFoo, bar=Bar(id=1, name=bar)), Foo(name=fortyFoo, bar=Bar(id=1, name=bar))]

这是期望的结果,其中“ bar”仅在第二次反序列化时才第一次被定义为对象。

何时:

val l2 = firstListDeserialized + listOf(f3, f4)

退出:

First list serialized:
[{"name":"firstFoo","bar":{"id":1,"name":"bar"}},{"name":"secondFoo","bar":1}]

First list desserialized:
[Foo(name=firstFoo, bar=Bar(id=1, name=bar)), Foo(name=secondFoo, bar=Bar(id=1, name=bar))]

Second list serialized:
[{"name":"firstFoo","bar":{"id":1,"name":"bar"}},{"name":"secondFoo","bar":1},{"name":"thirdFoo","bar":{"id":1,"name":"bar"}},{"name":"fortyFoo","bar":1}]

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `io.ubivis.ubivismobile.ExampleUnitTest$Bar`, problem: Already had POJO for id (java.lang.Integer) [[ObjectId: key=1, type=com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator, scope=io.ubivis.ubivismobile.ExampleUnitTest$Bar]]
 at [Source: (String)"[{"name":"firstFoo","bar":{"id":1,"name":"bar"}},{"name":"secondFoo","bar":1},{"name":"thirdFoo","bar":{"id":1,"name":"bar"}},{"name":"fortyFoo","bar":1}]"; line: 1, column: 124] (through reference chain: java.util.ArrayList[2]->io.ubivis.ubivismobile.ExampleUnitTest$Foo["bar"])

这就是我得到的。注意,“ bar”在第二个序列化的“ firstFoo”和“ thirdFoo”中定义为对象。还要注意,“ firstListDeserialized”是一个不同的列表,不包含“ f1”和“ f2”(它们只是具有相同值的对象),并且是由“ l1”的反序列化导致的。我无法使用第一个解决方案,因为“ f1”和“ f2”将来自API调用,因此它们已经被序列化了。

0 个答案:

没有答案