DEV Community

Marcos Nathan
Marcos Nathan

Posted on

How to Configure Kotlinx Serialization in Spring Boot MVC Rest Client

The Kotlinx Serialization is a powerful and efficient library for serializing and deserializing Kotlin objects. In this tutorial, we'll learn how to configure it for use with RestClient.

1. Add dependencies

Check the latest version Kotlinx serialization plugin

plugins {
    ...
    kotlin("plugin.serialization") version "2.1.0"
}
Enter fullscreen mode Exit fullscreen mode

Check the latest version Kotlin serialization library

dependecies {
    ...
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
}
Enter fullscreen mode Exit fullscreen mode

2. Create a MessageConverter configuration

import kotlinx.serialization.json.Json
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.http.converter.KotlinSerializationStringHttpMessageConverter
import org.springframework.http.converter.json.KotlinSerializationJsonHttpMessageConverter

@Configuration
class KtxConfig {

    @Bean
    fun ktxMessageConverter() : KotlinSerializationJsonHttpMessageConverter {
        // if you want to ignore unknown keys from json string,
        // otherwise make sure your data class has all json keys.
        val json = Json { ignoreUnknownKeys = true }
        return KotlinSerializationJsonHttpMessageConverter(json)
    }

    @Bean
    fun ktxMessageConverterWithMediaType() : KotlinSerializationStringHttpMessageConverter<Json> {
        // if you want to ignore unknown keys from json string,
        // otherwise make sure your data class has all json keys.
        val json = Json { ignoreUnknownKeys = true }
        return object : KotlinSerializationStringHttpMessageConverter<Json>(json, MediaType.TEXT_HTML, MediaType("text", "html")){}
    }

}
Enter fullscreen mode Exit fullscreen mode

3. Create a serializable Data Class

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class ZipCode(
    @SerialName("cep")
    val zipCode: String,
    @SerialName("logradouro")
    val street: String,
    @SerialName("uf")
    val stateAcron: String,
    @SerialName("estado")
    val state: String,
    @SerialName("regiao")
    val region: String,
)
Enter fullscreen mode Exit fullscreen mode

In this example I'm using a ZipCode JSON free API ViaCep

4. Setup your RestClient object

import com.ktxserializationrestclientspringboot.beans.KtxConfig
import com.ktxserializationrestclientspringboot.json.ZipCode
import org.springframework.stereotype.Component
import org.springframework.web.client.RestClient


@Component
class ViaCepGateway(
    private val ktxConfig: KtxConfig
) : ZipCodeGateway {

    private val restClient: RestClient = RestClient.builder()
        .messageConverters {
            it.add(0, ktxConfig.ktxMessageConverter())
            it.add(1, ktxConfig.ktxMessageConverterWithMediaType())
        }
        .baseUrl("https://viacep.com.br/ws/")
        .build()


    override fun getInfoCep(cep: String): ZipCode? {
        return restClient.get()
            .uri("/{cep}/json", cep)
            .retrieve()
            .onStatus({ it.is4xxClientError || it.is5xxServerError }, {_, response ->
                throw RuntimeException("Fail to find $cep informations. status code: ${response.statusCode}")
            })
            .body(ZipCode::class.java)
    }
}
Enter fullscreen mode Exit fullscreen mode

In this step make sure you have added the proper MessageConverter

.messageConverters {
    it.add(0, ktxConfig.ktxMessageConverter())
    //it.add(1, ktxConfig.ktxMessageConverterWithMediaType())  only one is needed
}
Enter fullscreen mode Exit fullscreen mode

5. Call your ZipCodeGateway

@Component
class MainCommandLineRunner(
    private val getCep: GetZipCode
): CommandLineRunner {

    private companion object {
        val logger: Logger = LoggerFactory.getLogger(MainCommandLineRunner::class.java)
    }

    override fun run(vararg args: String?) {
        val zipCodeInfo = getCep.execute("01001000")
        logger.info(zipCodeInfo.toString())
    }
}
Enter fullscreen mode Exit fullscreen mode

Output should be like

ZipCode(zipCode=01001-000, street=Praça da , stateAcron=SP, state=São Paulo, region=Sudeste)
Enter fullscreen mode Exit fullscreen mode

Bonus

If you need to convert the RestClient's response to a list of entities, you'll need to use the ParameterizedTypeReference interface like this:

return restClient.get()
...
.body(object : ParameterizedTypeReference<List<YOUR_CLASS>>() {})
Enter fullscreen mode Exit fullscreen mode

The complete implementation is available here. Feel free to explore and try it out!

Top comments (0)