6

We have a usecase where the JSON returned by the endpoint has to be serialized differently based on the endpoint. Is it possible to register two separate ObjectMapper beans and specify which one to use for a specific controller? For example, if I define a custom objectmapper as shown below, can I ask Spring Boot to use this mapper to serialize only the return objects from ControllerTwo but use the default/Primary objectmapper for serializing objects returned from ContorllerOne?

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper mapper = builder.build();
        return mapper;
    }

    @Bean
    public ObjectMapper objectMapperCustom(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper mapper = builder.build();
        //customize mapper
        return mapper;
    }

    @RestController
    public class ControllerOne {

       @GetMapping("/personNew/{id}")
       public Person getMyClass() {
    
       }
    }

    @RestController
    public class ControllerTwo {

       @GetMapping("/personOld/{id}")
       public Person getMyClass() {
    
       }
    }
RKodakandla
  • 3,318
  • 13
  • 59
  • 79

1 Answers1

8

As of 2021 unanswered means it is still not (easy) possible to use an alternative ObjectMapper for the same media/object/response type. I.e.

No, it is not (easy) possible to:

... register Controller specific ObjectMapper in SpringBoot

(..not without "re-implementing" half of ).

But what is (easy) possible:

Is to register custom ObjectMapper per java- and media type (combinations! + wildcards for the media types)! (additionally to the spring-configured "default object mapper")

With:

    //no bean
    private ObjectMapper fooMapper() {
        return new ObjectMapper()
                .configure(SerializationFeature.WRAP_ROOT_VALUE, true)
                .configure(SerializationFeature.INDENT_OUTPUT, false)
                .setDateFormat(new SimpleDateFormat("dd.MM.yyyy hh:mm:ss"));
    }

    //no bean
    private ObjectMapper barMapper() {
        return new ObjectMapper()
                .configure(SerializationFeature.WRAP_ROOT_VALUE, true)
                .configure(SerializationFeature.INDENT_OUTPUT, false)
                .setDateFormat(new SimpleDateFormat("MM/dd/yyyy K:mm:ss a"));
    }

we can do:

@Bean //overwrites the autoconfigured 
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(
     @Autowired ObjectMapper objectMapper // auto- and property configured, when no (alternative) ObjectMapper bean is defined.
) { 
    MappingJackson2HttpMessageConverter bean = new MappingJackson2HttpMessageConverter();
    // set "the default":
    bean.setObjectMapper(objectMapper);

    // register custom:
    bean.registerObjectMappersForType(Foo.class, m -> { //!!
        m.put(MediaType.APPLICATION_JSON, fooMapper());
        // ...
    });
    bean.registerObjectMappersForType(Bar.class, m -> {
        m.put(MediaType.APPLICATION_JSON, barMapper());
    });
    return bean;
}

The trick here is not to let (directly) manage the object mappers, but to register "plain ones", since all the "spring.jackson.* magic" will be skipped, once an ObjectMapper bean is present.


To achieve custom "views" of (e.g.) Person, introduced (2014) JSON Views.

Víctor Herraiz
  • 1,132
  • 1
  • 11
  • 26
xerx593
  • 12,237
  • 5
  • 33
  • 64
  • 1
    hey thanks for posting answer. It has been long time and forgot to update it. I actually ended up with similar approach in my app. I have registered two ObjectMapper beans - one default build of Jackon2ObjectMapperBuilder and other one is customized. Then appropriate bean is auowired where needed. – RKodakandla May 28 '21 at 14:55
  • thanks for the accept! :-) I came across through [this](https://stackoverflow.com/q/67695180/592355). So the requirement seems still actual & "popular". – xerx593 May 28 '21 at 15:04