How to customise the Jackson JSON mapper in Spring Web MVC
Spring MVC with annotations is great for AJAX + JSON until you need to customise the JSON mapping.
If you are using Spring 3.1 take a look at configuring a message converter as part of the mvc namespace config for a more elegant solution.
There is an option to add annotations directly to your model classes, but if you want to keep them separate you can do the following:
Create a custom mapper class
Section titled “Create a custom mapper class”In this example we are using joda-time’s LocalDate, and want to
serialize this to as a "yyyy-MM-dd" formatted String, instead of the provided 3 field mapping.
public class LocalDateSerializer extends SerializerBase<LocalDate> {
public LocalDateSerializer() { super(LocalDate.class); }
@Override public void serialize(LocalDate value, JsonGenerator jgen, SerializerProvider provider) throws IOException { jgen.writeString(value.toString()); // this returns the date as "yyyy-MM-dd" }
@Override public JsonNode getSchema(SerializerProvider provider, Type typeHint) throws JsonMappingException { throw new UnsupportedOperationException(); }}Create a custom ObjectMapper
Section titled “Create a custom ObjectMapper”Create a custom ObjectMapper by extending the default Jackson ObjectMapper.
@Componentpublic class CustomObjectMapper extends ObjectMapper { public CustomObjectMapper() { CustomSerializerFactory sf = new CustomSerializerFactory(); sf.addSpecificMapping(LocalDate.class, new LocalDateSerializer()); this.setSerializerFactory(sf); }}Register classes in the Spring context
Section titled “Register classes in the Spring context”Register the new mapper at start up, by getting at the beans that were created by
<mvc:annotation-driven />
@Componentpublic class JacksonFix { private AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter; private CustomObjectMapper objectMapper;
@PostConstruct public void init() { HttpMessageConverter<?>[] messageConverters = annotationMethodHandlerAdapter.getMessageConverters(); for (HttpMessageConverter<?> messageConverter : messageConverters) { if (messageConverter instanceof MappingJacksonHttpMessageConverter) { MappingJacksonHttpMessageConverter m = (MappingJacksonHttpMessageConverter) messageConverter; m.setObjectMapper(objectMapper); } } }
// this will exist due to the <mvc:annotation-driven/> bean @Autowired public void setAnnotationMethodHandlerAdapter(AnnotationMethodHandlerAdapter annotationMethodHandlerAdapter) { this.annotationMethodHandlerAdapter = annotationMethodHandlerAdapter; }
@Autowired public void setObjectMapper(CustomObjectMapper objectMapper) { this.objectMapper = objectMapper; }}If you are using IntelliJ, you can add a suppress warning annotation at the top of the file:
@SuppressWarnings({"SpringJavaAutowiringInspection"})Spring will set the 2 fields first, then run the init() method. It will ‘dig’ at the existing beans and find the Jackson converter, then replace the default object mapper with the custom object mapper.