micronaut-serialization
micronaut-serialization copied to clipboard
Data Binding using Kotlin data class will ignore constructor defaults
Expected Behavior
It should be possible to bind JSON to a Kotlin data class with default value(s) in the constructor without providing values (in the JSON) for the already defined constructor defaults.
Having this data class:
@Serdeable
data class Foo(
val bar: String,
var validFrom: LocalDate = LocalDate.now(),
)
And the following controller:
@Controller("/foos")
class FooController {
@Post
fun save(@Body foo: Foo): HttpResponse<Foo> {
return HttpResponse.created(foo)
}
}
This JSON should be valid to create an instance of Foo
when posting to /foos
:
{
"bar": "some_value"
}
Actual Behaviour
When trying to bind JSON (see example above) to a Kotlin data class containing a default value in the constructor, I will get the following error when posting a JSON without an explicit value for the non-nullable (here validFrom
) property:
"Failed to convert argument [foo] for value [null] due to: Unable to deserialize type [com.example.Foo]. Required constructor parameter [LocalDate validFrom] at index [1] is not present or is null in the supplied data"
Steps To Reproduce
- Clone example application
- Run test using
./gradlew test
- Test will fail caused by a
Bad Request
Or
- Clone example application
- Run application using
./gradlew run
- Post a JSON with
validFrom
value will return a JSON representation of theFoo
instance:
curl --location 'http://localhost:8080/foos' \
--header 'Content-Type: application/json' \
--data '{
"bar": "some_value",
"validFrom": "2023-11-10"
}'
// Response
{
"bar": "some_value",
"validFrom": "2023-11-10"
}
- Post a JSON without
validFrom
value will return a400 Bad Request
:
curl --location 'http://localhost:8080/foos' \
--header 'Content-Type: application/json' \
--data '{
"bar": "some_value"
}'
// Response
{
"_links": {
"self": [
{
"href": "/foos",
"templated": false
}
]
},
"_embedded": {
"errors": [
{
"message": "Failed to convert argument [foo] for value [null] due to: Unable to deserialize type [com.example.Foo]. Required constructor parameter [LocalDate validFrom] at index [1] is not present or is null in the supplied data",
"path": "/foo"
}
]
},
"message": "Bad Request"
}
Environment Information
- Operating System: Ubuntu 23.10
- JDK Version: 17.0.8.1-zulu
Example Application
https://github.com/davidsonnabend/micronaut-serialization-kotlin-constructor-defaults-deserialization
Version
4.1.6
Try declare the Foo class to this:
@Serdeable
data class Foo(
val bar: String,
var validFrom: LocalDate? = LocalDate.now(),
)
Try declare the Foo class to this:
@Serdeable data class Foo( val bar: String, var validFrom: LocalDate? = LocalDate.now(), )
This is a valid workaround but it would be nice if this will also work with non-nullable types.