How to customise the Jackson JSON mapper implicitly used by Spring Boot?
Asked Answered
L

15

125

I'm using Spring Boot (1.2.1), in a similar fashion as in their Building a RESTful Web Service tutorial:

@RestController
public class EventController {
   @RequestMapping("/events/all")
   EventList events() {
       return proxyService.getAllEvents();
   }
}

So above, Spring MVC implicitly uses Jackson for serialising my EventList object into JSON.

But I want to do some simple customisations to the JSON format, such as:

setSerializationInclusion(JsonInclude.Include.NON_NULL)

Question is, what is the simplest way to customise the implicit JSON mapper?

I tried the approach in this blog post, creating a CustomObjectMapper and so on, but the step 3, "Register classes in the Spring context", fails:

org.springframework.beans.factory.BeanCreationException: 
  Error creating bean with name 'jacksonFix': Injection of autowired dependencies failed; 
  nested exception is org.springframework.beans.factory.BeanCreationException: 
  Could not autowire method: public void com.acme.project.JacksonFix.setAnnotationMethodHandlerAdapter(org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter); 
  nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: 
  No qualifying bean of type [org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter]   
  found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

It looks like those instructions are for older versions of Spring MVC, while I'm looking for a simple way to get this working with latest Spring Boot.

Laural answered 4/2, 2015 at 14:54 Comment(2)
Do you have this Annotation inserted?: @SuppressWarnings({"SpringJavaAutowiringInspection"})Capitol
Notice that if you are using Spring Web as well you will need to tell it manually to use this ObjectMapper otherwise it will create it own instance which will not be configured. See #7854530Nuggar
K
137

You can configure property inclusion, and numerous other settings, via application.properties:

spring.jackson.default-property-inclusion=non_null

There's a table in the documentation that lists all of the properties that can be used.

If you want more control, you can also customize Spring Boot's configuration programatically using a Jackson2ObjectMapperBuilderCustomizer bean, as described in the documentation:

The context’s Jackson2ObjectMapperBuilder can be customized by one or more Jackson2ObjectMapperBuilderCustomizer beans. Such customizer beans can be ordered (Boot’s own customizer has an order of 0), letting additional customization be applied both before and after Boot’s customization.

Lastly, if you don't want any of Boot's configuration and want to take complete control over how the ObjectMapper is configured, declare your own Jackson2ObjectMapperBuilder bean:

@Bean
Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    // Configure the builder to suit your needs
    return builder;
}
Karlykarlyn answered 4/2, 2015 at 15:23 Comment(10)
Alright, this seems to work. Where would you put such a method? Perhaps in a main Application class (with @ComponentScan, @EnableAutoConfiguration etc)?Laural
Yes. This method can go in any @Configuration class in your app. The main Application class is a good place for it.Karlykarlyn
Important note: The Jackson2ObjectMapperBuilder class is part of the spring-web component and was added in version 4.1.1.Emilyemina
@AndyWilkinson With this configuration, you can't use anymore Spring boot auto-configuration for Jackson isn't it ?Freberg
@Freberg Correct. If you're happy using a milestone, Spring Boot 1.3 adds support for configuring serialisation inclusion via application.properties: github.com/spring-projects/spring-boot/commit/…Karlykarlyn
@Deprecated setSerializationInclusionKatleen
Deprecated: ObjectMapper.setSerializationInclusion was deprecated in Jackson 2.7 ... use https://mcmap.net/q/179688/-how-to-customise-the-jackson-json-mapper-implicitly-used-by-spring-boot spring.jackson.default-property-inclusion=non_null insteadDunseath
"If you're using an earlier version of Spring Boot" made me believe it wouldn't work with Spring Boot 2.0.1, but it worked. Thanks!Calculated
I am having a problem with this approach of configuring the ObjectMapper in Spring Boot. Spring Boot does its own configuration of the ObjectMapper with its own Serializers/Deserializers for e.g. LocalDateTime. When defining your own Jackson2ObjectMapperBuilder you are loosing this Spring configuration.Pegmatite
There are more properties than just the six mentioned in the linked "How-to" guide. A full list is available a the reference documentation appendex (currently eleven spring.jackson.* options are mentioned).Biostatics
S
32

I am answering bit late to this question, but someone, in future, might find this useful. The below approach, besides lots of other approaches, works best, and I personally think would better suit a web application.

@Configuration
@EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {

 ... other configurations

@Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
        builder.serializationInclusion(JsonInclude.Include.NON_NULL);
        builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
        builder.serializationInclusion(Include.NON_EMPTY);
        builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
    }
}
Schach answered 12/6, 2015 at 10:30 Comment(2)
In recent version you must implement WebMvcConfigurerArea
yes, peoples, try this solution, I've tried to provide Bean for ObjectMapper, then Bean for Jackson2ObjectMapperBuilder buy that didn't work and I still have no idea why. Appending coverter worked!Thury
B
27

The documentation states several ways to do this.

If you want to replace the default ObjectMapper completely, define a @Bean of that type and mark it as @Primary.

Defining a @Bean of type Jackson2ObjectMapperBuilder will allow you to customize both default ObjectMapper and XmlMapper (used in MappingJackson2HttpMessageConverter and MappingJackson2XmlHttpMessageConverter respectively).

Borrego answered 4/2, 2015 at 15:23 Comment(1)
How to do the same without replacing the default ObjectMapper ? I mean keeping the default as well a custom.Henka
M
24

A lot of things can configured in applicationproperties. Unfortunately this feature only in Version 1.3, but you can add in a Config-Class

@Autowired(required = true)
public void configureJackson(ObjectMapper jackson2ObjectMapper) {
    jackson2ObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}

[UPDATE: You must work on the ObjectMapper because the build()-method is called before the config is runs.]

Madea answered 2/10, 2015 at 13:4 Comment(1)
That was the solution that saved my day. Except I did add this method to the REST controller itself, rather than to the configuration class.Annetteannex
F
14

You can add a following method inside your bootstrap class which is annotated with @SpringBootApplication

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper objectMapper = builder.createXmlMapper(false).build();
    objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);

    objectMapper.registerModule(new JodaModule());

    return objectMapper;
}
Frivolous answered 26/9, 2018 at 3:10 Comment(1)
This one worked for me using Boot 2.1.3. The spring.jackson properties had no effect.Azalea
D
11

spring.jackson.serialization-inclusion=non_null used to work for us

But when we upgraded spring boot version to 1.4.2.RELEASE or higher, it stopped working.

Now, another property spring.jackson.default-property-inclusion=non_null is doing the magic.

in fact, serialization-inclusion is deprecated. This is what my intellij throws at me.

Deprecated: ObjectMapper.setSerializationInclusion was deprecated in Jackson 2.7

So, start using spring.jackson.default-property-inclusion=non_null instead

Dunseath answered 23/5, 2017 at 14:42 Comment(0)
L
7

I stumbled upon another solution, which is quite nice.

Basically, only do step 2 from the blog posted mentioned, and define a custom ObjectMapper as a Spring @Component. (Things started working when I just removed all the AnnotationMethodHandlerAdapter stuff from step 3.)

@Component
@Primary
public class CustomObjectMapper extends ObjectMapper {
    public CustomObjectMapper() {
        setSerializationInclusion(JsonInclude.Include.NON_NULL); 
        configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 
    }
}

Works as long as the component is in a package scanned by Spring. (Using @Primary is not mandatory in my case, but why not make things explicit.)

For me there are two benefits compared to the other approach:

  • This is simpler; I can just extend a class from Jackson and don't need to know about highly Spring-specific stuff like Jackson2ObjectMapperBuilder.
  • I want to use the same Jackson configs for deserialising JSON in another part of my app, and this way it's very simple: new CustomObjectMapper() instead of new ObjectMapper().
Laural answered 4/2, 2015 at 17:9 Comment(2)
The downside to this approach is that your custom configuration won't be applied to any ObjectMapper instances that are created or configured by Spring Boot.Karlykarlyn
Hmm, the custom config is used for the implicit serialisation in @RestController classes which currently suffices for me. (So you mean those instances are created by Spring MVC, not Spring Boot?) But if I run into other cases where ObjectMappers need to be instantiated, I'll keep the Jackson2ObjectMapperBuilder approach in mind!Laural
C
5

When I tried to make ObjectMapper primary in spring boot 2.0.6 I got errors So I modified the one that spring boot created for me

Also see https://mcmap.net/q/181988/-how-to-configure-jackson-in-spring-boot-application-without-overriding-springs-default-setting-in-pure-java

@Lazy
@Autowired
ObjectMapper mapper;

@PostConstruct
public ObjectMapper configureMapper() {
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

    mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, true);
    mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);

    SimpleModule module = new SimpleModule();
    module.addDeserializer(LocalDate.class, new LocalDateDeserializer());
    module.addSerializer(LocalDate.class, new LocalDateSerializer());
    mapper.registerModule(module);

    return mapper;
}
Carabao answered 15/5, 2019 at 16:17 Comment(0)
P
3

The right way to add further configurations to the Spring Boot peconfigured ObjectMapper is to define a Jackson2ObjectMapperBuilderCustomizer. Else you are overwriting Springs configuration, which you do not want to lose.

 @Configuration
public class MyJacksonConfigurer implements Jackson2ObjectMapperBuilderCustomizer {
    @Override
    public void customize(Jackson2ObjectMapperBuilder builder) {
        builder.deserializerByType(LocalDate.class, new MyOwnJsonLocalDateTimeDeserializer());
    }
}
Pegmatite answered 20/11, 2020 at 17:27 Comment(0)
D
1

I found the solution described above with :

spring.jackson.serialization-inclusion=non_null

To only work starting at the 1.4.0.RELEASE version of spring boot. In all other cases the config is ignored.

I verified this by experimenting with a modification of the spring boot sample "spring-boot-sample-jersey"

Decile answered 12/8, 2016 at 18:49 Comment(0)
S
1

I've seen numerous questions regarding this issue. This is what worked for me in Spring Boot version 2.7.0-SNAPSHOT.

I created a configuration, MapperConfigs, created a objectMapper bean, annotated primary as the documentation says

@Configuration
@Log4j2
public class MapperConfigs {
    @Bean
    @Primary
    ObjectMapper objectMapper() {
        log.info("Object mapper overrides ");
        return JsonMapper.builder()
                .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES)
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .build();
    }
}

I then @Autowired objectMapper. See below:

@Service
public class GenerateRequestUniqueID {
    @Autowired
    ObjectMapper objectMapper;
    ...
}
Systematics answered 9/3, 2022 at 11:30 Comment(0)
M
0

I know the question asking for Spring boot, but I believe lot of people looking for how to do this in non Spring boot, like me searching almost whole day.

Above Spring 4, there is no need to configure MappingJacksonHttpMessageConverter if you only intend to configure ObjectMapper.

You just need to do:

public class MyObjectMapper extends ObjectMapper {

    private static final long serialVersionUID = 4219938065516862637L;

    public MyObjectMapper() {
        super();
        enable(SerializationFeature.INDENT_OUTPUT);
    }       
}

And in your Spring configuration, create this bean:

@Bean 
public MyObjectMapper myObjectMapper() {        
    return new MyObjectMapper();
}
Melanson answered 19/10, 2017 at 6:44 Comment(0)
H
0

Remove the default one and add your custom converter:

@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
//        Remove the default MappingJackson2HttpMessageConverter
        converters.removeIf(converter -> {
            String converterName = converter.getClass().getSimpleName();
            return converterName.equals("MappingJackson2HttpMessageConverter");
        });
//        Add your custom MappingJackson2HttpMessageConverter
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        converter.setObjectMapper(objectMapper);
        converters.add(converter);
        WebMvcConfigurer.super.extendMessageConverters(converters);
    }
}

Note: Please don't use configureMessageConverters() instead of extendMessageConverters() from WebMvcConfigurer because configure method will remove all the existing converters which will be installed by default.

Hope it will help someone like me who has wasted some hours debugging the issue :)

Husserl answered 24/8, 2022 at 18:44 Comment(0)
J
0

There are two ways to customize Jackson ObjectMapper:-

  1. Override the default behavior of auto-configured ObjectMapper by Spring Boot
  2. Overwrite the ObjectMapper to have a complete control

Override ObjectMapper

@Configuration
public class CustomJacksonConfig {

    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
        return builder.build().setSerializationInclusion(JsonInclude.Include.NON_NULL)
                .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
                .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .configure(SerializationFeature.INDENT_OUTPUT, true)
                .registerModule(new JavaTimeModule());
    }
}

Overwrite ObjectMapper

@Configuration
public class CustomJacksonConfig {

    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        return new ObjectMapper()
                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
                .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true)
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .configure(SerializationFeature.INDENT_OUTPUT, true);
    }
}

source: https://codingnconcepts.com/spring-boot/customize-jackson-json-mapper/

Jeremiahjeremias answered 16/12, 2022 at 12:38 Comment(0)
B
0

Spring Boot

Most of the config can be done with the spring.jackson.* props (see Common Application Properties).

If you need more fine-grained control, you can provide a Jackson2ObjectMapperBuilderCustomizer :

@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
    return new Jackson2ObjectMapperBuilderCustomizer() {
        @Override
        public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
            //customize     
        }
    };
}

Spring Web MVC

When @EnableWebMvc comes into play, the settings will not affect the HttpMessageConverts out of the box (see explanation below). To make Spring Web MVC honor the spring.jackson.default-property-inclusion property (and all other spring.jackson.* props) you'd have to apply the Jackson2ObjectMapperBuilder to the message converter:

@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {

    @Autowired
    private Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder;

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.stream()
            .filter(MappingJackson2HttpMessageConverter.class::isInstance)
            .map(MappingJackson2HttpMessageConverter.class::cast)
            .findFirst()
            .ifPresent(converter -> converter.setObjectMapper(jackson2ObjectMapperBuilder.build()));
    }
}

Background: When using spring-web-mvc and @EnableWebMVc, the message converters will be created in WebMvcConfigurationSupport.addDefaultHttpMessageConverters() with Jackson2ObjectMapperBuilder.json() instead of using the Jackson2ObjectMapperBuilder bean that Spring Boot has configured through JacksonAutoConfiguration.

Bane answered 2/5 at 12:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.