Affects: 5.3.1
Bug
I'm using the kotlinx-serialization library. The library is properly set up, jackson is excluded from dependencies, and returning a class annotated with @Serializable
works correctly.
I'm trying to return a simple Kotlin list from a controller. When accessing the route the server displays this error and nothing is returned:
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
@RestController
class Controller {
@GetMapping("/")
fun notworking() = listOf(1, 2, 3)
}
Error message
2020-11-24 17:28:51.929 WARN 189362 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class java.util.Arrays$ArrayList]
2020-11-24 17:28:51.945 WARN 189362 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class java.util.LinkedHashMap]
What I expected
I expected the list to be serialized properly like this:
[1,2,3]
Workarounds
Those three workarounds work:
@Serializable
data class Wrapper(val content: List<Int>)
@GetMapping("/")
fun working() = Wrapper(listOf(1, 2, 3))
@GetMapping("/", produces = ["application/json"])
fun working() = Json.encodeToString(listOf(1, 2, 3))
@GetMapping("/working")
fun working() = Json.encodeToJsonElement(listOf(1, 2, 3))
Comment From: sdeleuze
We should probably take advantage of GenericHttpMessageConverter
here not just the boolean supports(Class<?> clazz)
variant that does not carry generic type informations.
Let's also make sure it allows both Jackson and Kotlin serialization to be used together is a Boot app with actuator as discussed in https://github.com/spring-projects/spring-boot/issues/24238.
Comment From: Aelerinya
I noticed also that error messages returned by the server too cannot be encoded when jackson is disabled, as described in this page
When a request in forbidden in a REST controller the server sends back this:
{"timestamp":"2020-11-25T22:29:58.573+00:00","status":403,"error":"Forbidden","message":"","path":"/user"}
But when kotlinx-serialization is enabled and jackson disabled, the server resolved the request with this error:
org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class java.util.LinkedHashMap
Maybe the documentation could be updated in the meantime to specify that disabling jackson as said prevents internal messages from being serialized ?
Comment From: sdeleuze
Yeah we probably need to advise (and maybe configure) both by default since Kotlin serialization is only for Kotlin classes annotated with @Serializable
.
Comment From: mickael-coquer
Hi @sdeleuze ,
Migrating to Spring Boot 2.4.1, I encountered the following error while trying to deserialize JSON
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Polymorphic serializer was not found for missing class discriminator ('null')
... nested exception is kotlinx.serialization.json.internal.JsonDecodingException: Polymorphic serializer was not found for missing class discriminator ('null')
I happen to have both Jackson and kotlinx-serialization in the classpath and Kotlin takes precedence over Jackson.
In my projet we're using Java interfaces implemented by Java classes.
I guess the issue comes from the KotlinSerializationJsonHttpMessageConverter.candRead method that returns true when the Type is a Java interface. But when the KotlinSerializationJsonHttpMessageConverter.read method is called then it fails because the concrete type is a Java class and not a Kotlin class.
This is not blocking for me as I can change the converters order or just remove kotlinx-serialization (it's a transitive dependency I don't actually need). But I thought you might want to be aware of this behavior.
Comment From: sdeleuze
@mickael-coquer Thanks for raising that, worth to fix IMO, could you please create a related issue?
Comment From: mickael-coquer
Here you go: https://github.com/spring-projects/spring-framework/issues/26298