Configuring ObjectMapper in Spring

JavaJsonSpringJacksonObject Object-Mapping

Java Problem Overview


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][1] which says how to configurate the objectmapper.

I included the custom objectmapper as described [here][2].

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

Does anybody have a hint? Thanks in advance

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

[1]: http://wiki.fasterxml.com/JacksonFeatureAutoDetect "explanation" [2]: https://stackoverflow.com/questions/3591291/spring-jackson-and-customization-e-g-customdeserializer

Java Solutions


Solution 1 - Java

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;
    }
}

Solution 2 - Java

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" />

Solution 3 - Java

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:

Solution 4 - Java

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;
		}
	}

}

Solution 5 - Java

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.

Solution 6 - Java

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>   

Solution 7 - Java

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.

Solution 8 - Java

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

Solution 9 - Java

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.

Solution 10 - Java

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

Solution 11 - Java

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

Solution 12 - Java

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!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSteve EastwoodView Question on Stackoverflow
Solution 1 - JavaAlexView Answer on Stackoverflow
Solution 2 - JavaAndrew NewdigateView Answer on Stackoverflow
Solution 3 - JavagavenkoaView Answer on Stackoverflow
Solution 4 - JavaAram KocharyanView Answer on Stackoverflow
Solution 5 - JavaAdrianView Answer on Stackoverflow
Solution 6 - JavaSteve EastwoodView Answer on Stackoverflow
Solution 7 - Javauser11153View Answer on Stackoverflow
Solution 8 - JavajecyhwView Answer on Stackoverflow
Solution 9 - JavaDormouseView Answer on Stackoverflow
Solution 10 - JavaCorayThanView Answer on Stackoverflow
Solution 11 - JavaSam YCView Answer on Stackoverflow
Solution 12 - JavaCarlos AGView Answer on Stackoverflow