Kotlin Serialization with Spring 5.3

Kotlin Serialization with Spring 5.3

Spring 5.3 adds the support of Kotlin serialization.

Spring Web MVC

KotlinSerializationJsonHttpMessageConverter is the HttpMessageConverter implementation that can read and write JSON using Kotlin serialization. If Kotlin serialization class kotlinx.serialization.json.Json is in the classpath, then this converter will be registered automatically.

This converter tries to get the KSerializer object for a given type to determine if an object can be serialized/deserialized using Kotlin serialization. Resolved serializers are put into the cache for later use.

private KSerializer<Object> serializer(Type type) {
  KSerializer<Object> serializer = serializerCache.get(type);
  if (serializer == null) {
    serializer = SerializersKt.serializer(type);
    serializerCache.put(type, serializer);
  }
  return serializer;
}

When you have both Kotlin serialization and Jackson on the classpath, Kotlin serialization will be checked first. This is because KotlinSerializationJsonHttpMessageConverter appears before Jackson in the list. See the picture below.

Web MVC converters

Note

However, this only applies to Spring 5.3.2. If you are using Spring 5.3.1, you'll find out that Jackson will be added instead of Kotlin serialization. Make sure that you using Spring 5.3.2 or higer versions, if you want to have both Kotlin serialization and Jackson in the classpath.

Use Serializers Module

If you are using your own serializers module, you need to create your own KotlinSerializationJsonHttpMessageConverter object.

The Kotlin code belows shows an example of open polymorphism with custom serializers module.

@Serializable
abstract class Base {
    abstract val name: String
}

@Serializable
class Child1(override val name: String, val v1: String) : Base()

@Serializable
class Child2(override val name: String, val v2: String) : Base()

@Serializable
data class InheritanceData(
    @Polymorphic val base: Base,
    val extra: Int
)

val customModule = SerializersModule {
    polymorphic(Base::class) {
        subclass(Child1::class)
        subclass(Child2::class)
    }
}

To use this serializers module, we need to update HttpMessageConverter objects. In the WebConfig below, we override extendMessageConverters method to replace existing KotlinSerializationJsonHttpMessageConverter with a new object.

See this post for more details about customizing HttpMessageConverter objects.

@Configuration
class WebConfig : WebMvcConfigurer {
    override fun extendMessageConverters(converters: MutableList<HttpMessageConverter<*>>) {
        val converter = KotlinSerializationJsonHttpMessageConverter(Json {
            serializersModule = customModule
            ignoreUnknownKeys = true
        })
        converters.forEachIndexed { index, httpMessageConverter ->
            if (httpMessageConverter is KotlinSerializationJsonHttpMessageConverter) {
                converters[index] = converter
                return
            }
        }
    }
}

See GitHub for the complete code.

Spring WebFlux

For Spring WebFlux, KotlinSerializationJsonEncoder and KotlinSerializationJsonDecoder are used for encoding and decoding respectively.

RestTemplate

When using RestTemplate to send requests, you need to be careful when both Kotlin serialization and Jackson are in the classpath. RestTemplate checks Jackson first and only adds one converter for JSON encoding/decoding.

To solve this issue, you can force RestTemplate to use KotlinSerializationJsonHttpMessageConverter by passing a list of HttpMessageConverter objects.

val restTemplate = RestTemplate(listOf(KotlinSerializationJsonHttpMessageConverter()))

Source code

See GitHub.

© 2023 VividCode