Configuring ObjectMapper in Spring
Asked Answered
F

13

114

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();
    }
}
Farrar answered 21/10, 2011 at 18:59 Comment(0)
T
161

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;
    }
}
Trix answered 29/9, 2015 at 11:39 Comment(11)
Spring supports customize some properties of Jackson in application.properties: docs.spring.io/spring-boot/docs/current/reference/html/…Postrider
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 annotationVipul
Please refer to my answer if you're not using Spring Boot.Trochlear
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?Mulford
Is it really working when using also @EnableWebMvc? I doubt because ... see in my answer: Why @EnableWebMvc usage is a problem?Seraglio
06-May-2019 19:16:09.383 [main] INFO i.n.epp.config.JacksonConfiguration - Using custom Jackson ObjectMapper :)Mulford
@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.Mulford
Hm, pretty crazy considering the content of WebMvcConfigurationSupport as I detailed in my response.Seraglio
No longer works as of Spring Boot 2.2.x. Instead use @Bean public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder)Houseclean
@Postrider Maybe it's a silly question. The document only writes "<feature_name>", but I wonder if there is a full list of "<feature_name>"s? For example, if I wanna set "DefaultTyping", what "<feature_name>" shall I use?Slicker
This solution did not work for me because as @EnableWebMvc is enabled. Solution 2 worked for me in this answerInsect
H
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" />
Habakkuk answered 22/12, 2011 at 15:44 Comment(2)
This helped for using Jackson 2.x with Spring 3.1: #10420540Hege
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.7Tantrum
H
25

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:

Helsell answered 13/3, 2017 at 7:33 Comment(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).Hearttoheart
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().Seraglio
H
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;
        }
    }

}
Hege answered 27/9, 2012 at 9:11 Comment(0)
S
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.

Seraglio answered 2/5, 2019 at 19:19 Comment(3)
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!Claudio
I had an issue where I couldn't customize my ObjectMapper that was miserable b/c of EnableWebMvc. So glad I stumbled across this.Conundrum
Yeah, solution 2 worked for me. ThanksInsect
F
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>   
Farrar answered 31/10, 2011 at 9:21 Comment(2)
Thank you very much for the solution! I have spen over four hours digging around <annotation-driven>, it simply did not pick up the converters. Tried your answer, finally it works!Vernita
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.Phrase
C
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.

Cropdusting answered 12/2, 2014 at 11:24 Comment(0)
P
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

Pak answered 12/5, 2016 at 10:23 Comment(0)
T
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.

Trochlear answered 21/3, 2018 at 10:57 Comment(5)
I would prefer to have the ObjectMapper injected and configured via Spring Boot.Faulty
Sure, if you use Spring Boot. This was not a Spring Boot question.Trochlear
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.Faulty
This would clear all default message converters! I'm pretty sure no one wants this. See my answer for the solution.Seraglio
Well if you'd want to extend the message converters the correct solution would be to override extendMessageConverters, as per Spring documentation. But I haven't had any need for it, because I just need a customized MappingJackson2HttpMessageConverter.Trochlear
H
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()
Houseclean answered 27/3, 2020 at 10:39 Comment(1)
Can you explain why?Platus
T
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();
}
Tjaden answered 19/10, 2017 at 6:41 Comment(2)
Does not work - - customObjectMapper: defined by method 'customObjectMapper' in class path resource [com/Config.class] - _halObjectMapper: defined in nullHagy
And this will only work with Spring Boot. Not plain Spring Web.Trochlear
T
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!

Trinatrinal answered 18/3, 2014 at 11:24 Comment(0)
I
0

If you are using Spring WebFlux, @EnableWebFlux overrides any custom settings like spring.jackson.serialization.write-dates-as-timestamps=false, so you need to override WebFlux's ObjectMapper instance according to WebFlux doc.

@Configuration
public class WebfluxConfig implements WebFluxConfigurer {
    private final ObjectMapper objectMapper;

    @Autowired
    public WebfluxConfig(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
        configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper));
        configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper));
    }
}

Imperative answered 22/3, 2024 at 17:21 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.