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