FasterXML / jackson-module-kotlin

Module that adds support for serialization/deserialization of Kotlin (http://kotlinlang.org) classes and data classes.
Apache License 2.0
1.12k stars 175 forks source link

Incosistent behaviour with a single field #771

Closed alturkovic closed 3 weeks ago

alturkovic commented 7 months ago

Search before asking

Describe the bug

The example below will not deserialize a single-field data class with a default value:

fun main() {
    val mapper = jacksonObjectMapper()
    val testing = Testing()
    val json = mapper.writeValueAsString(testing)
    println(json) // {} works correctly
    println(mapper.readValue<Testing>(json)) // fails
}

data class Testing(
    private val random: Random = Random
)

But if I add another field to class, it works as expected:

fun main() {
    val mapper = jacksonObjectMapper()
    val testing = Testing()
    val json = mapper.writeValueAsString(testing)
    println(json) // {"name":"John Doe"} works correctly
    println(mapper.readValue<Testing>(json)) // Testing(name=John Doe, random=kotlin.random.Random$Default@2e77b8cf)
}

data class Testing(
    val name: String = "John Doe",
    private val random: Random = Random
)

I have also noticed that it works with a single primitive field:

fun main() {
    val mapper = jacksonObjectMapper()
    val testing = Testing()
    val json = mapper.writeValueAsString(testing)
    println(json) // {} works correctly
    println(mapper.readValue<Testing>(json)) // Testing(name=John Doe)
}

data class Testing(
    private val name: String = "John Doe"
)

Another example is with @JacksonInject:

fun main() {
    val mapper = ObjectMapper()
        .registerModule(kotlinModule {
            enable(KotlinFeature.NullIsSameAsDefault)
        })
        .setInjectableValues(InjectableValues.Std().addValue(String::class.java, "Example"))
    val testing = mapper.readValue<Testing>("{}") // fails
    println(testing.id)
}

data class Testing(
    @JacksonInject val id: String
)

But adding another field do the data class fixes the problem:

fun main() {
    val mapper = ObjectMapper()
        .registerModule(kotlinModule {
            enable(KotlinFeature.NullIsSameAsDefault)
        })
        .setInjectableValues(InjectableValues.Std().addValue(String::class.java, "Example"))
    val testing = mapper.readValue<Testing>("{}") // works
    println(testing.id) // Example
}

data class Testing(
    val name: String = "John Doe", 
    @JacksonInject val id: String
)

To Reproduce

No response

Expected behavior

No response

Versions

Kotlin: 1.9.20 Jackson-module-kotlin: 2.16.0 Jackson-databind: 2.16.0

Additional context

No response

alturkovic commented 1 month ago

Any updates?

k163377 commented 3 weeks ago

@alturkovic Sorry for the delay.

I just checked on branch 2.18 and it succeeded in all cases.

package com.fasterxml.jackson.module.kotlin

import kotlin.random.Random

fun main() {
    val mapper = jacksonObjectMapper()
    val testing = Testing()
    val json = mapper.writeValueAsString(testing)
    println(json) // {} works correctly
    println(mapper.readValue<Testing>(json)) // Testing(random=kotlin.random.Random$Default@57459491)
}

data class Testing(
    private val random: Random = Random
)
package com.fasterxml.jackson.module.kotlin

import com.fasterxml.jackson.annotation.JacksonInject
import com.fasterxml.jackson.databind.InjectableValues
import com.fasterxml.jackson.databind.ObjectMapper

fun main() {
    val mapper = ObjectMapper()
        .registerModule(kotlinModule {
            enable(KotlinFeature.NullIsSameAsDefault)
        })
        .setInjectableValues(InjectableValues.Std().addValue(String::class.java, "Example"))
    val testing = mapper.readValue<Testing>("{}") // Example
    println(testing.id)
}

data class Testing(
    @JacksonInject val id: String
)

If this has been fixed, could you please close it?

alturkovic commented 3 weeks ago

I just tested it with 2.18.0-rc1 and it worked. Thank you! Closing the issue.