Search code examples
spring-bootjackson

Is it possible to register Controller specific ObjectMapper in SpringBoot


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() {
    
       }
    }

Solution

  • 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.