17
votes

I have mixins configured in my objectmapperbuilder config, using the regular spring web controller, the data outputted according to the mixins. However using webflux, a controller with a method returning a Flow or Mono have the data serialized like if the objectmapper a default one.

How to get webflux to enforce an objectmapper configuration to be used ?

sample config:

@Bean
JavaTimeModule javatimeModule(){
    return new JavaTimeModule();
}

@Bean
Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(){
return jacksonObjectMapperBuilder ->  jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                                                                    .mixIn(MyClass.class, MyClassMixin.class);
}
5

5 Answers

16
votes

I actually found my solution by stepping through the init code:

@Configuration
public class Config {

    @Bean
    JavaTimeModule javatimeModule(){
        return new JavaTimeModule();
    }

    @Bean
    Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(){
    return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.featuresToEnable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .mixIn(MyClass.class, MyClassMixin.class);
    }


    @Bean
    Jackson2JsonEncoder jackson2JsonEncoder(ObjectMapper mapper){
       return new Jackson2JsonEncoder(mapper);
    }

    @Bean
    Jackson2JsonDecoder jackson2JsonDecoder(ObjectMapper mapper){
        return new Jackson2JsonDecoder(mapper);
    }

    @Bean
    WebFluxConfigurer webFluxConfigurer(Jackson2JsonEncoder encoder, Jackson2JsonDecoder decoder){
        return new WebFluxConfigurer() {
            @Override
            public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
                configurer.defaultCodecs().jackson2Encoder(encoder);
                configurer.defaultCodecs().jackson2Decoder(decoder);
            }
        };

    }
}
10
votes

I translated the solution of @Alberto Galiana to Java and injected the configured Objectmapper for convenience, so you avoid having to do multiple configurations:

@Configuration
@RequiredArgsConstructor
public class WebFluxConfig implements WebFluxConfigurer {

    private final ObjectMapper objectMapper;

    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
        configurer.defaultCodecs().jackson2JsonEncoder(
            new Jackson2JsonEncoder(objectMapper)
        );

        configurer.defaultCodecs().jackson2JsonDecoder(
            new Jackson2JsonDecoder(objectMapper)
        );
    }
}
8
votes

Just implement WebFluxConfigurer and override method configureHttpMessageCodecs

Sample code for Spring Boot 2 + Kotlin

@Configuration
@EnableWebFlux
class WebConfiguration : WebFluxConfigurer {

    override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
        configurer.defaultCodecs().jackson2JsonEncoder(Jackson2JsonEncoder(ObjectMapper()
                .setSerializationInclusion(JsonInclude.Include.NON_EMPTY)))

        configurer.defaultCodecs().jackson2JsonDecoder(Jackson2JsonDecoder(ObjectMapper()
                .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)))
    }
}

Make sure all your data classes to be encoded/decoded have all its properties annotated with @JsonProperty even if property name is equal in class and json data

data class MyClass(
    @NotNull
    @JsonProperty("id")
    val id: String,

    @NotNull
    @JsonProperty("my_name")
    val name: String)
0
votes

I have tried all the different solutions (@Primary @Bean for ObjectMapper, configureHttpMessageCodecs(), etc.). What worked for me at the end was specifying a MIME type. Here's an example:

@Configuration
class WebConfig: WebFluxConfigurer {
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
        val encoder = Jackson2JsonEncoder(objectMapper, MimeTypeUtils.APPLICATION_JSON)
        val decoder = Jackson2JsonDecoder(objectMapper, MimeTypeUtils.APPLICATION_JSON)
        configurer.defaultCodecs().jackson2JsonEncoder(encoder)
        configurer.defaultCodecs().jackson2JsonDecoder(decoder)
    }
}

0
votes

In my case, I was trying to use a customized ObjectMapper while inheriting all of the behavior from my app's default WebClient.

I found that I had to use WebClient.Builder.codecs. When I used WebClient.Builder.exchangeStrategies, the provided overrides were ignored. Not sure if this behavior is something specific to using WebClient.mutate, but this is the only solution I found that worked.

WebClient customizedWebClient = webClient.mutate()
                                         .codecs(clientCodecConfigurer -> 
                                                     clientCodecConfigurer.defaultCodecs()
                                                                          .jackson2JsonDecoder(new Jackson2JsonDecoder(customObjectMapper)))
                                         .build();