jackson-module-kotlin icon indicating copy to clipboard operation
jackson-module-kotlin copied to clipboard

Exception when using @JsonIdentityInfo

Open RaoPrashanth opened this issue 3 years ago • 2 comments

Describe the bug The issue that I am facing similar to Issue 194 but extended. When I try to use the similar setup but json value would not have id property ("{"name": "Foo"}") because that would be automatically created in constructor using UUID.randomUUID().

To Reproduce

class TestGithub571 {
    private val id: UUID = UUID.fromString("149800a6-7855-4e09-9185-02e442da8013")
    private val json = """{"name": "Foo"}"""

    @Test
    fun testIdentityInfo() {
        val mapper = jacksonObjectMapper()
        val value = mapper.readValue(json, WithIdentity::class.java)
        assertEquals(id, value.id)
        assertEquals("Foo", value.name)
    }

    @JsonIdentityInfo(
        property = "id",
        scope = WithIdentity::class,
        generator = ObjectIdGenerators.PropertyGenerator::class
    )
    class WithIdentity(val id: UUID = UUID.randomUUID(),
                       val name: String)

    @Test
    fun testIdentityInfo_WithDefaultId() {
        val mapper = jacksonObjectMapper()
        val value = mapper.readValue(json, WithIdentityAndDefaultId::class.java)
        assertEquals(id, value.id)
        assertEquals("Foo", value.name)
    }

    @JsonIdentityInfo(
        property = "id",
        scope = WithIdentityAndDefaultId::class,
        generator = ObjectIdGenerators.PropertyGenerator::class
    )
    class WithIdentityAndDefaultId(val id: UUID = UUID.fromString("149800a6-7855-4e09-9185-02e442da8013"),
                                   val name: String)
}

Expected behavior I expected the above test cases to get passed because the value for name is deserialized from json string and UUID is constructed from class constructor.

Versions Kotlin: 1.6.21 Jackson-module-kotlin: 2.13.2 Jackson-databind: 2.13.2.1

RaoPrashanth avatar Jun 11 '22 18:06 RaoPrashanth

By specifying ObjectIdGenerators.None for generator, the deserialization appears to succeed. https://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-annotations/latest/com/fasterxml/jackson/annotation/JsonIdentityInfo.html

companion object {
    private val defaultId: UUID = UUID.fromString("149800a6-7855-4e09-9185-02e442da8013")
    private const val json = """{"name": "Foo"}"""
}

@JsonIdentityInfo(
    property = "id",
    scope = WithIdentity::class,
    generator = ObjectIdGenerators.None::class
)
class WithIdentity(val id: UUID = defaultId, val name: String)

@Test
fun testIdentityInfo() {
    val mapper = jacksonObjectMapper()
    val value = mapper.readValue(json, WithIdentity::class.java)
    assertEquals(defaultId, value.id)
    assertEquals("Foo", value.name)
}

However, I am not sure if this is the correct usage. I am not familiar with this feature, but from what I see in some of the samples, I think there is something wrong with what you are trying to do.

@cowtowncoder Do you know how to achieve a use case like "when deserializing a class with JsonIdentityInfo, if there is no property with id in the JSON, generate a value for it" when using only databind?

k163377 avatar Jun 04 '23 06:06 k163377

@k163377 I can't think of an obvious way -- this is not a planned or supported usage for @JsonIdentityInfo. Identity is meant to be preserved on serialiation, reconstructed on deserialization; this is the canonical use case.

This is not to say there might not be some clever usage, or combination of settings, but I cannot immediately think of the way as it goes outside of planned usage.

cowtowncoder avatar Jun 05 '23 01:06 cowtowncoder