Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data Binding using Kotlin data class will ignore constructor defaults #666

Open
davidsonnabend opened this issue Nov 13, 2023 · 2 comments

Comments

@davidsonnabend
Copy link

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

  1. Clone example application
  2. Run test using ./gradlew test
  3. Test will fail caused by a Bad Request

Or

  1. Clone example application
  2. Run application using ./gradlew run
  3. Post a JSON with validFrom value will return a JSON representation of the Foo 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"
}
  1. Post a JSON without validFrom value will return a 400 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

@yazidIT
Copy link

yazidIT commented Dec 29, 2023

Try declare the Foo class to this:

@Serdeable
data class Foo(
    val bar: String,
    var validFrom: LocalDate? = LocalDate.now(),
)

@davidsonnabend
Copy link
Author

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants