soufrk
soufrk

Reputation: 835

Custom ObjectMapper overrides SpringBoot default ObjectMapper

I have a Spring-Boot controller, which in turn makes a REST call to another service. Hence, there is a RestTemplate client inside the controller, which needs a Json-Root-Value-Wrap-And-Unwrap functionality.

Following common suggestions, I made a custome ObjectMapper like

@Configuration
public class CustomJsonMapper {

  @Bean
  public Jackson2ObjectMapperBuilder jacksonBuilder() {
      Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
      builder.featuresToEnable(DeserializationFeature.UNWRAP_ROOT_VALUE);
      return builder;
  }

}

So I can auto-wire this class, and use in the RestTemplate, which is inside the controller. Hence the Controller looks somewhat like,

@RestController
@RequestMapping("uat")
public class UatController {

    @Autowired
    CustomObjectMapper customMapper;

    @PostMapping(value = "startWork")
    public ResponseEntity<?> startWork(@RequestBody startWorkInput startWorkInput) {
        // Build the request
        RestTemplate restTemp = new RestTemplate(factory);
        restTemp.getMessageConverters().removeIf(m -> m.getClass().getName().equals(MappingJackson2HttpMessageConverter.class.getName()));
        restTemp.getMessageConverters().add(new MappingJackson2HttpMessageConverter(customMapper));
        // Proceed to make client call
    }
}

But, this causes the default ObjectMapper in SpringBoot to be over-ridden, I guess. Which is why I need to wrap the root value of input to this service as well.

Is there a way to use custom ObjectMapper without over-riding the default one.

Upvotes: 1

Views: 951

Answers (1)

Karol Dowbecki
Karol Dowbecki

Reputation: 44962

If the mapper is localised to just one bean you can declare it as a field and not as a separate bean:

@RestController
@RequestMapping("uat")
public class UatController {

  private final ObjectMapper customMapper = Jackson2ObjectMapperBuilder.json()
      .featuresToEnable(DeserializationFeature.UNWRAP_ROOT_VALUE)
      .build();

  ...

If you need to reuse you can try redefining the ObjectMapper beans and marking the default one @Primary:

@Configuration
public class CustomJsonMapper {

  @Bean
  @Primary
  public ObjectMapper objectMapper() {
    return Jackson2ObjectMapperBuilder.json().build();
  }

  @Bean
  public ObjectMapper customObjectMapper() {
    return Jackson2ObjectMapperBuilder.json()
        .featuresToEnable(DeserializationFeature.UNWRAP_ROOT_VALUE)
        .build();
  }
}

however it's more complex than just localising the changes to the single controller that requires it. Moreover, it goes against the convention of auto-configuration in JacksonAutoConfiguration.JacksonObjectMapperBuilderConfiguration class.

Upvotes: 2

Related Questions