113

My goal is to configure the objectMapper in the way that it only serialises element which are annotated with @JsonProperty.

In order to do so I followed this explanation which says how to configurate the objectmapper.

I included the custom objectmapper as described here.

However, when the class NumbersOfNewEvents is serialized it still contains all attributes in the json.

Why is this happening, and how can I get the desired result?

Jackson 1.8.0 spring 3.0.5

CustomObjectMapper

public class CompanyObjectMapper extends ObjectMapper {
    public CompanyObjectMapper() {
        super();
        setVisibilityChecker(getSerializationConfig()
                .getDefaultVisibilityChecker()
                .withCreatorVisibility(JsonAutoDetect.Visibility.NONE)
                .withFieldVisibility(JsonAutoDetect.Visibility.NONE)
                .withGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withIsGetterVisibility(JsonAutoDetect.Visibility.NONE)
                .withSetterVisibility(JsonAutoDetect.Visibility.DEFAULT));
    }
}

servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:component-scan base-package="de.Company.backend.web" />
 
    <mvc:annotation-driven />
    
    <bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                    <property name="objectMapper" ref="jacksonObjectMapper" />
                </bean>
            </list>
        </property>
    </bean>
    
    <bean id="jacksonObjectMapper" class="de.Company.backend.web.CompanyObjectMapper" />
</beans>

NumbersOfNewEvents

public class NumbersOfNewEvents implements StatusAttribute {

    public Integer newAccepts;
    public Integer openRequests;
    
    public NumbersOfNewEvents() {
        super();
    }
}
starball
  • 20,030
  • 7
  • 43
  • 238
Steve Eastwood
  • 1,783
  • 3
  • 16
  • 21

12 Answers12

160

Using Spring Boot (1.2.4) and Jackson (2.4.6) the following annotation based configuration worked for me.

@Configuration
public class JacksonConfiguration {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);

        return mapper;
    }
}
Alex
  • 1,664
  • 1
  • 11
  • 3
  • 5
    Spring supports customize some properties of Jackson in application.properties: https://docs.spring.io/spring-boot/docs/current/reference/html/howto-spring-mvc.html#howto-customize-the-jackson-objectmapper – NangSaigon Feb 18 '17 at 06:59
  • 21
    Hey, guys. I faced a problem that when you declare bean with name objectMapper in java config, just as it is showed in example, this bean is ignored by Spring Boot, cause bean with such name is already registered in the system, even if you place \@Primary annotation on it. To fix this problem you need to register bean with different name, e.g. jsonObjectMapper, and mark it with \@Primary annotation – Demwis Apr 19 '18 at 18:54
  • Please refer to [my answer](https://stackoverflow.com/a/49404496/883603) if you're **not** using Spring Boot. – Dormouse Apr 04 '19 at 19:58
  • A question: you must name the class `JacksonConfiguration`? It's an automagical way to instruct Spring Boot to use this class as configuration class for Jackson? – Marco Sulla Apr 30 '19 at 19:32
  • 1
    Is it really working when using also `@EnableWebMvc`? I doubt because ... see in [my answer](https://stackoverflow.com/questions/7854030/configuring-objectmapper-in-spring/55958912#55958912): *Why @EnableWebMvc usage is a problem?* – Adrian May 02 '19 at 19:32
  • `06-May-2019 19:16:09.383 [main] INFO i.n.epp.config.JacksonConfiguration - Using custom Jackson ObjectMapper` :) – Marco Sulla May 06 '19 at 17:21
  • @adrhc: I'm using Spring Boot and it seems it adds `@EnableWebMvc` by default. So this solution seems to work every time. See this [SO question and answers](https://stackoverflow.com/questions/51008382/why-spring-boot-application-doesnt-require-enablewebmvc). – Marco Sulla May 06 '19 at 17:33
  • Hm, pretty crazy considering the content of `WebMvcConfigurationSupport` as I detailed in my response. – Adrian May 06 '19 at 17:36
  • No longer works as of Spring Boot `2.2.x`. Instead use `@Bean public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder)` – CorayThan Mar 27 '20 at 10:36
  • @NangSaigon Maybe it's a silly question. The document only writes "", but I wonder if there is a full list of ""s? For example, if I wanna set "DefaultTyping", what "" shall I use? – Zhou Aug 23 '20 at 08:47
  • This solution did not work for me because as `@EnableWebMvc` is enabled. Solution 2 worked for me in [this answer](https://stackoverflow.com/a/55958912/2462531) – Shailendra Madda Dec 08 '21 at 17:22
27

It may be because I'm using Spring 3.1 (instead of Spring 3.0.5 as your question specified), but Steve Eastwood's answer didn't work for me. This solution works for Spring 3.1:

In your spring xml context:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
        <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
            <property name="objectMapper" ref="jacksonObjectMapper" />
        </bean>        
    </mvc:message-converters>
</mvc:annotation-driven>

<bean id="jacksonObjectMapper" class="de.Company.backend.web.CompanyObjectMapper" />
Andrew Newdigate
  • 6,005
  • 3
  • 37
  • 31
  • This helped for using Jackson 2.x with Spring 3.1: http://stackoverflow.com/questions/10420040/jackson-2-0-with-spring-3-1 – Aram Kocharyan Sep 07 '12 at 04:34
  • Useful! Had to adopt this approach to registering my custom ObjectMapper on upgrading from Spring 3.0 to 3.2. Had previously been setting an objectMapper property when defining a MappingJacksonJsonView bean, but that no longer worked. This is with Jackson 1.7 – David Easley Jan 03 '18 at 19:02
23

There is org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean for a long time. Starting from 1.2 release of Spring Boot there is org.springframework.http.converter.json.Jackson2ObjectMapperBuilder for Java Config.

In String Boot configuration can be as simple as:

spring.jackson.deserialization.<feature_name>=true|false
spring.jackson.generator.<feature_name>=true|false
spring.jackson.mapper.<feature_name>=true|false
spring.jackson.parser.<feature_name>=true|false
spring.jackson.serialization.<feature_name>=true|false
spring.jackson.default-property-inclusion=always|non_null|non_absent|non_default|non_empty

in classpath:application.properties or some Java code in @Configuration class:

@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.indentOutput(true).dateFormat(new SimpleDateFormat("yyyy-MM-dd"));
    return builder;
}

See:

Community
  • 1
  • 1
gavenkoa
  • 45,285
  • 19
  • 251
  • 303
  • 2
    This works for most use cases, however the Spring Boot jackson configuration is ignored if you're using the @EnableWebMvc annotation (tested in Spring Boot 2.0.3). – John Jul 26 '18 at 15:21
  • 1
    Though I didn't test I'm sure that won't work with @EnableWebMvc (despite using or not spring boot). Why? see WebMvcConfigurationSupport (DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport): `Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json()`. – Adrian May 02 '19 at 18:50
16

I've used this with Jackson 2.x and Spring 3.1.2+

servlet-context.xml:

Note that the root element is <beans:beans>, so you may need to remove beans and add mvc to some of these elements depending on your setup.

    <annotation-driven>
        <message-converters>
            <beans:bean
                class="org.springframework.http.converter.StringHttpMessageConverter" />
            <beans:bean
                class="org.springframework.http.converter.ResourceHttpMessageConverter" />
            <beans:bean
                class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <beans:property name="objectMapper" ref="jacksonObjectMapper" />
            </beans:bean>
        </message-converters>
    </annotation-driven>

    <beans:bean id="jacksonObjectMapper"
        class="au.edu.unimelb.atcom.transfer.json.mappers.JSONMapper" />

au.edu.unimelb.atcom.transfer.json.mappers.JSONMapper.java:

public class JSONMapper extends ObjectMapper {

    public JSONMapper() {
        SimpleModule module = new SimpleModule("JSONModule", new Version(2, 0, 0, null, null, null));
        module.addSerializer(Date.class, new DateSerializer());
        module.addDeserializer(Date.class, new DateDeserializer());
        // Add more here ...
        registerModule(module);
    }

}

DateSerializer.java:

public class DateSerializer extends StdSerializer<Date> {

    public DateSerializer() {
        super(Date.class);
    }

    @Override
    public void serialize(Date date, JsonGenerator json,
            SerializerProvider provider) throws IOException,
            JsonGenerationException {
        // The client side will handle presentation, we just want it accurate
        DateFormat df = StdDateFormat.getBlueprintISO8601Format();
        String out = df.format(date);
        json.writeString(out);
    }

}

DateDeserializer.java:

public class DateDeserializer extends StdDeserializer<Date> {

    public DateDeserializer() {
        super(Date.class);
    }

    @Override
    public Date deserialize(JsonParser json, DeserializationContext context)
            throws IOException, JsonProcessingException {
        try {
            DateFormat df = StdDateFormat.getBlueprintISO8601Format();
            return df.parse(json.getText());
        } catch (ParseException e) {
            return null;
        }
    }

}
Aram Kocharyan
  • 20,165
  • 11
  • 81
  • 96
14

SOLUTION 1

First (tested) working solution useful especially when using @EnableWebMvc:

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private ObjectMapper objectMapper;// created elsewhere
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // this won't add a 2nd MappingJackson2HttpMessageConverter 
        // as the SOLUTION 2 is doing but also might seem complicated
        converters.stream().filter(c -> c instanceof MappingJackson2HttpMessageConverter).forEach(c -> {
            // check default included objectMapper._registeredModuleTypes,
            // e.g. Jdk8Module, JavaTimeModule when creating the ObjectMapper
            // without Jackson2ObjectMapperBuilder
            ((MappingJackson2HttpMessageConverter) c).setObjectMapper(this.objectMapper);
        });
    }

SOLUTION 2

Of course the common approach below works too (also working with @EnableWebMvc):

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private ObjectMapper objectMapper;// created elsewhere
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // this will add a 2nd MappingJackson2HttpMessageConverter 
        // (additional to the default one) but will work and you 
        // won't lose the default converters as you'll do when overwriting
        // configureMessageConverters(List<HttpMessageConverter<?>> converters)
        // 
        // you still have to check default included
        // objectMapper._registeredModuleTypes, e.g.
        // Jdk8Module, JavaTimeModule when creating the ObjectMapper
        // without Jackson2ObjectMapperBuilder
        converters.add(new MappingJackson2HttpMessageConverter(this.objectMapper));
    }

Why @EnableWebMvc usage is a problem?

@EnableWebMvc is using DelegatingWebMvcConfiguration which extends WebMvcConfigurationSupport which does this:

if (jackson2Present) {
    Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
    if (this.applicationContext != null) {
        builder.applicationContext(this.applicationContext);
    }
    messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}

which means that there's no way of injecting your own ObjectMapper with the purpose of preparing it to be used for creating the default MappingJackson2HttpMessageConverter when using @EnableWebMvc.

Adrian
  • 3,321
  • 2
  • 29
  • 46
  • 1
    Great! Actually RepositoryRestMvcConfiguration, part of spring-data-rest-webmvc:3.2.3 doesn't use ObjectMapper created by Spring and only custom objectMapper with modules don't work out-of-the-box. First approach are actual and replace ObjectMapper customized by spring-data-rest-webmvc. Thanks! – Valtoni Boaventura Apr 17 '20 at 15:04
  • 1
    I had an issue where I couldn't customize my ObjectMapper that was miserable b/c of EnableWebMvc. So glad I stumbled across this. – Evan Brooks Aug 18 '20 at 21:56
  • 1
    Yeah, solution 2 worked for me. Thanks – Shailendra Madda Dec 08 '21 at 16:38
10

I found the solution now based on https://github.com/FasterXML/jackson-module-hibernate

I extended the object mapper and added the attributes in the inherited constructor.

Then the new object mapper is registered as a bean.

<!-- https://github.com/FasterXML/jackson-module-hibernate -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <array>
            <bean id="jsonConverter"
            class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                <property name="objectMapper">
                    <bean class="de.company.backend.spring.PtxObjectMapper"/>
                </property>
            </bean>
        </array>
    </property>
</bean>   
Steve Eastwood
  • 1,783
  • 3
  • 16
  • 21
  • Thank you very much for the solution! I have spen over four hours digging around ****, it simply did not pick up the converters. Tried your answer, finally it works! – snowindy Feb 15 '13 at 10:32
  • 1
    To use annotations, you could just annotate your custom Mapper that extends `ObjectMapper` as `@Component` and `@Primary`. This is the approach recommended in the Spring Reference Guide and it has worked fine for me. – OlgaMaciaszek Apr 26 '15 at 09:16
9

If you want to add custom ObjectMapper for registering custom serializers, try my answer.

In my case (Spring 3.2.4 and Jackson 2.3.1), XML configuration for custom serializer:

<mvc:annotation-driven>
    <mvc:message-converters register-defaults="false">
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
                    <property name="serializers">
                        <array>
                            <bean class="com.example.business.serializer.json.CustomObjectSerializer"/>
                        </array>
                    </property>
                </bean>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

was in unexplained way overwritten back to default by something.

This worked for me:

CustomObject.java

@JsonSerialize(using = CustomObjectSerializer.class)
public class CustomObject {

    private Long value;

    public Long getValue() {
        return value;
    }

    public void setValue(Long value) {
        this.value = value;
    }
}

CustomObjectSerializer.java

public class CustomObjectSerializer extends JsonSerializer<CustomObject> {

    @Override
    public void serialize(CustomObject value, JsonGenerator jgen,
        SerializerProvider provider) throws IOException,JsonProcessingException {
        jgen.writeStartObject();
        jgen.writeNumberField("y", value.getValue());
        jgen.writeEndObject();
    }

    @Override
    public Class<CustomObject> handledType() {
        return CustomObject.class;
    }
}

No XML configuration (<mvc:message-converters>(...)</mvc:message-converters>) is needed in my solution.

user11153
  • 8,536
  • 5
  • 47
  • 50
7

I am using Spring 4.1.6 and Jackson FasterXML 2.1.4.

    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                        <!-- 设置不输出null字段-->
                        <property name="serializationInclusion" value="NON_NULL"/>
                    </bean>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

this works at my applicationContext.xml configration

jecyhw
  • 141
  • 1
  • 6
4

To configure a message converter in plain spring-web, in this case to enable the Java 8 JSR-310 JavaTimeModule, you first need to implement WebMvcConfigurer in your @Configuration class and then override the configureMessageConverters method:

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(new JavaTimeModule(), new Jdk8Module()).build()
            .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    converters.add(new MappingJackson2HttpMessageConverter(objectMapper));
}

Like this you can register any custom defined ObjectMapper in a Java-based Spring configuration.

Dormouse
  • 1,617
  • 1
  • 23
  • 33
  • I would prefer to have the ObjectMapper injected and configured via Spring Boot. – Constantino Cronemberger Feb 19 '19 at 13:46
  • 3
    Sure, if you use Spring Boot. This was not a Spring Boot question. – Dormouse Feb 20 '19 at 08:06
  • Yes, interesting to notice that when using Spring Web with Spring Boot it does not use the ObjectMapper provided by Spring Boot and that is why I ended up in this question which helped me. – Constantino Cronemberger Feb 21 '19 at 12:53
  • This would clear all default message converters! I'm pretty sure no one wants this. See [my answer](https://stackoverflow.com/questions/7854030/configuring-objectmapper-in-spring/55958912#55958912) for the solution. – Adrian May 02 '19 at 19:29
  • Well if you'd want to extend the message converters the correct solution would be to override `extendMessageConverters`, as per [Spring documentation](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-config-message-converters). But I haven't had any need for it, because I just need a customized `MappingJackson2HttpMessageConverter`. – Dormouse May 03 '19 at 07:25
4

In Spring Boot 2.2.x you need to configure it like this:

@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
    return builder.build()
}

Kotlin:

@Bean
fun objectMapper(builder: Jackson2ObjectMapperBuilder) = builder.build()
CorayThan
  • 17,174
  • 28
  • 113
  • 161
2

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

(configure MappingJacksonHttpMessageConverter will cause you to lose other MessageConverter)

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();
}
Sam YC
  • 10,725
  • 19
  • 102
  • 158
1

I am using Spring 3.2.4 and Jackson FasterXML 2.1.1.

I have created a custom JacksonObjectMapper that works with explicit annotations for each attribute of the Objects mapped:

package com.test;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class MyJaxbJacksonObjectMapper extends ObjectMapper {

public MyJaxbJacksonObjectMapper() {

    this.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
            .setVisibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.ANY)
            .setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE)
            .setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE)
            .setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE);

    this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
    }
}

Then this is instantiated in the context-configuration (servlet-context.xml):

<mvc:annotation-driven>
    <mvc:message-converters>

        <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <beans:property name="objectMapper">
                <beans:bean class="com.test.MyJaxbJacksonObjectMapper" />
            </beans:property>
        </beans:bean>

    </mvc:message-converters>
</mvc:annotation-driven>

This works fine!

Carlos AG
  • 1,078
  • 1
  • 12
  • 16