While Kotlin Serialization can be used for controllers, it doesn't work to serialize DTOs returned from Actuator @Endpoints.
When setting spring.http.converters.preferred-json-mapper=gson (or jsonb), Gson is used for controllers and actuator endpoints.
When setting spring.http.converters.preferred-json-mapper=kotlin-serialization, Kotlin Serialization is used for controllers, but Jackson is used for actuator endpoints.
This demo project (sorry that it's in Kotlin, but Kotlin Serialization only works with Kotlin types) demonstrates the issue.
I did a bit of debugging: A breakpoint in org.springframework.http.converter.AbstractKotlinSerializationHttpMessageConverter#canWrite reveals:
In the WebMVC case:
- type is com.example.kotlin_serialization.MyDto
- clazz is class com.example.kotlin_serialization.MyDto
In the actuator case:
- type is java.lang.Object
- clazz is class com.example.kotlin_serialization.MyDto
And Spring Framework uses type to see if there's a generated serializer for the type (the serializers are generated by a compiler plugin, see build.gradle).
Comment From: wilkinsona
Generally speaking, when producing JSON, endpoints should return types that implement OperationResponseBody. Doing so should ensure that they're serialised using Jackson irrespective of the application's preferred JSON mapper. We want that to be the case with any alternative mapper (GSON, JSON-B, Kotlin Serialisation, etc) on the classpath.
Comment From: mhalbritter
Ah, thanks for the hint, i'll try that.
Nevertheless, I think there's a bug somewhere, because it behaves differently with gson and jsonb than with Kotlin Serialization.
Comment From: SeoHyeok2
Hi, I'd like to work on this issue. Could you please assign it to me?
Comment From: wilkinsona
Thanks for the offer, @SeoHyeok2, but we don't know what needs to be done here. Actuator is intentionally coupled to Jackson so the behavior with Kotlin Serialisation may not be a problem, although it is interesting that there's a mismatch as @mhalbritter described above.
Comment From: mhalbritter
If the DTO returned from the endpoint implements OperationResponseBody and spring.http.converters.preferred-json-mapper isn't set, then Jackson is used for Actuator and Kotlin Serialization is used for WebMVC, as it should be.
Setting spring.http.converters.preferred-json-mapper=kotlin-serialization isn't valid, as kotlin-serialization is no supported value. We only support jsonb, gson, jackson2, jackson.
Should we add kotlin-serialization support in spring.http.converters.preferred-json-mapper?
Comment From: wilkinsona
AIUI, you shouldn't need to set the property. The converters for Kotlin Serialisation are ordered so that they go before the more regular JSON converters. I think that's why "Kotlin Serialization is used for WebMVC" when the property is not set. What would be the intent of explicitly setting it?
Comment From: bclozel
At some point we had "kotlin-serialization" as a possible value for this property, but this was changed in https://github.com/spring-projects/spring-boot/issues/47178.
I'm not sure there is an arrangement where we can use kotlin-serialization in actuator and guarantee that all endpoint return values will be serializable. We can only guarantee the shape of the API response with Jackson, selecting Gson is already a questionable choice for many applications.
Comment From: mhalbritter
Ah, so it was there in the past.
What would be the intent of explicitly setting it?
I wanted to see if I can get Kotlin Serialization to also serialize endpoint responses like I can with gson or jsonb.
Comment From: mhalbritter
I'm not sure there is an arrangement where we can use kotlin-serialization in actuator and guarantee that all endpoint return values will be serializable. We can only guarantee the shape of the API response with Jackson, selecting Gson is already a questionable choice for many applications.
That makes sense. And with https://github.com/spring-projects/spring-boot/issues/47178, this issue is no longer valid.