micronaut-serialization icon indicating copy to clipboard operation
micronaut-serialization copied to clipboard

Micronaut Serde Deserialization Does Not Send Explicit Null JSON Parameters to Kotlin Data Class Constructor

Open joseFilA opened this issue 2 months ago • 5 comments

Expected Behavior

import io.micronaut.serde.annotation.Serdeable
import io.micronaut.test.annotation.TransactionMode
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
import jakarta.inject.Inject
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

@MicronautTest(transactionMode = TransactionMode.SINGLE_TRANSACTION)
class AssessmentDaoTest @Inject constructor(
    private val objectMapper: io.micronaut.serde.ObjectMapper
) {
    @Serdeable
    data class TestMe(
        val testString: String? = "I AM DEFAULT"
    )

    @Test
    fun testDefaultValues() = runTest {
        val jsonWithNull = """{"testString": null}"""
        val jsonWithMissing = """{}"""

        // With nullIsSameAsDefault = false, a null in JSON overrides the default
        val fromNull = objectMapper.readValue(jsonWithNull, TestMe::class.java)
        println(fromNull) // MyData(value=null)
        assertEquals(null, fromNull.testString) //FAILS - default value set

        // A missing field still uses the default value
        val fromMissing = objectMapper.readValue(jsonWithMissing, TestMe::class.java)

        println(fromMissing) // MyData(value=default)
        assertEquals("I AM DEFAULT", fromNull.testString)
    }
}

Actual Behaviour

I would expect the assertion above, assertEquals(null, fromNull.effectiveStartDate) to pass and null to be set for the parameter instead of the default value.

Steps To Reproduce

No response

Environment Information

In application.yml: micronaut.serde.serialization.inclusion: non_null

In build.gradle:

    // Serialization
    annotationProcessor mn.micronaut.serde.processor
    implementation mn.micronaut.serde.jackson

kotlinVersion=2.1.0 micronautVersion=4.7.4

Example Application

https://github.com/joseFilA/ExampleMicronautSerdeKotlinDataClassIssue/tree/main

Version

4.7.4

joseFilA avatar Oct 06 '25 16:10 joseFilA

Please create a sample app with the latest version that is reproducing it

dstepanov avatar Oct 06 '25 17:10 dstepanov

https://github.com/joseFilA/ExampleMicronautSerdeKotlinDataClassIssue/tree/main

There is a single test in the repo that shows the assertion failing.

joseFilA avatar Oct 06 '25 22:10 joseFilA

@dstepanov not sure if this needs to be bumped or not.

joseFilA avatar Oct 08 '25 13:10 joseFilA

I checked it but I'm not sure if this can be implemented easily. It's first time I see Kotlin works this way.

dstepanov avatar Oct 08 '25 14:10 dstepanov

Gotcha. It seems to work fine with the default Jackson ObjectMapper if that helps at all.

joseFilA avatar Oct 08 '25 15:10 joseFilA