spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

Provide a way to configure KotlinSerializationJsonDecoder

Open jamesward opened this issue 2 years ago • 4 comments

It seems there isn't a way to provide a custom Json or set config on Json for the KotlinSerializationJsonDecoder

Spring Framework 5.3.14

Workaround:

@Configuration(proxyBeanMethods = false)
class InitConfiguration {

    @ExperimentalSerializationApi
    @Bean
    fun kotlinSerializationJsonDecoder() = KotlinSerializationJsonDecoder(Json {
        explicitNulls = false
    })

}

@ExperimentalSerializationApi
@Configuration
class WebConfig(val decoder: KotlinSerializationJsonDecoder) : WebFluxConfigurer {
    override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
        super.configureHttpMessageCodecs(configurer)
        configurer.defaultCodecs().kotlinSerializationJsonDecoder(decoder)
    }
}

jamesward avatar Dec 22 '21 21:12 jamesward

I don't think the decoder needs to be a bean so the config could be simplified, and the result would be how this should be done. So it is possible but maybe you are expecting a method for this on CodecConfigurer.DefaultCodecs? The number of methods there would explode if we added options at this level, and more importantly it would force classpath dependencies that are otherwise optional.

rstoyanchev avatar Jan 05 '22 11:01 rstoyanchev

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

spring-projects-issues avatar Jan 12 '22 11:01 spring-projects-issues

I think you are right about the unnecessary bean but I guess I was hoping for something like Jackson2ObjectMapperBuilderCustomizer which provides a nice way to customize Jackson.

jamesward avatar Jan 15 '22 14:01 jamesward

I tried the same thing as james (without success, obviously):

@Configuration
@EnableWebFlux
class JsonConfiguration : WebFluxConfigurer {
    override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
        val decoder = KotlinSerializationJsonDecoder(Json { ignoreUnknownKeys = true })
        configurer.defaultCodecs().kotlinSerializationJsonDecoder(decoder)
    }
}

The configuration is called, but the configured KotlinSerializationJsonDecoder is not used for deserialization in an annotated rest service. It is always Json.Default being used.

knob-creek avatar Aug 10 '22 08:08 knob-creek

It seems there isn't a way to provide a custom Json or set config on Json for the KotlinSerializationJsonDecoder

Spring Framework 5.3.14

Workaround:

@Configuration(proxyBeanMethods = false)
class InitConfiguration {

    @ExperimentalSerializationApi
    @Bean
    fun kotlinSerializationJsonDecoder() = KotlinSerializationJsonDecoder(Json {
        explicitNulls = false
    })

}

@ExperimentalSerializationApi
@Configuration
class WebConfig(val decoder: KotlinSerializationJsonDecoder) : WebFluxConfigurer {
    override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
        super.configureHttpMessageCodecs(configurer)
        configurer.defaultCodecs().kotlinSerializationJsonDecoder(decoder)
    }
}

@jamesward Thanks for sharing your workaround. Unfortunately had no luck yet in doing the same.

+1 on this request. It would be great to have some customization option, perhaps properties also available at JSON Properties but something similar to Jackson2ObjectMapperBuilderCustomizer would be nice for this purpose as well.

I was wondering if @sdeleuze has any thoughts on this.

albertocavalcante avatar Aug 17 '22 04:08 albertocavalcante

I just realized that for what I wanted to do I had to take a different approach. I wanted to configure the kotlinx.serialization Decoder for WebClient.

I ended up with something close to this:

object KSerializationConfig {
    val json = Json {
        ignoreUnknownKeys = true
        isLenient = true
        allowSpecialFloatingPointValues = true
        useArrayPolymorphism = true
        encodeDefaults = true
        explicitNulls = false
    }
}
    @Bean
    fun apiClient() = WebClient.builder()
            .baseUrl("${apiProperties.baseUrl}/rest/api")
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .codecs {
                val decoder = KotlinSerializationJsonDecoder(KSerializationConfig.json)
                it.defaultCodecs().kotlinSerializationJsonDecoder(decoder)
            }.build()

Based on: https://stackoverflow.com/a/66020393/12249394 and https://discuss.kotlinlang.org/t/kotlinx-serialization-with-spring-boot/16540/15

albertocavalcante avatar Aug 17 '22 04:08 albertocavalcante

@knob-creek I tried this and it worked as expected

@Configuration
class JsonConfiguration : WebFluxConfigurer {
	override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
		val json = Json { ignoreUnknownKeys = true }
		configurer.defaultCodecs().kotlinSerializationJsonDecoder(KotlinSerializationJsonDecoder(json))
		configurer.defaultCodecs().kotlinSerializationJsonEncoder(KotlinSerializationJsonEncoder(json))
	}
}

I tend to think this is reasonably concise and discoverable to be usable.

@jamesward I am not against something like Jackson2ObjectMapperBuilderCustomizer but this is on Spring Boot side not Spring Framework, and I am not sure kotlinx.serialization is popular enough for the Spring Boot team to accept such dedicated support.

In any case, it looks like to me that this issue can be declined as I don't see something obvious we should do on Framework side.

sdeleuze avatar Jan 16 '23 18:01 sdeleuze

@sdeleuze Is there a way to configure this if I am not using WebFlux?

987Nabil avatar Mar 03 '23 05:03 987Nabil

If what you want is configuring this with Spring MVC, yes you can customize it via regular converter configuration, which may be a bit more involved than with codecs, but totally doable.

For both WebFlux and MVC, worth to have in mind that https://github.com/Kotlin/kotlinx.serialization/issues/2060 will be required to customize serialization of interface types.

sdeleuze avatar Mar 06 '23 09:03 sdeleuze