PDA

View Full Version : Jackson MappingJacksonHttpMessageConverter/ObjectMapper for Date not working



iktuz
Oct 12th, 2011, 12:24 PM
Hi All,

When I transport Date as JSON reponse from server to the client page, the format is serialized as timestamp by default, like numeric value. I am trying to customize another format using a Date mask (with SimpleDateFormat). I was digging around a way to customize a global JSON Date response formatter. I dont want to use annotation for all date bean fields. All the suggestions I have followed around internet have failed. The most curious is that no exception is raised.

I am using:


Spring 3.0.6
jackson-core-asl-1.4.2 (** I have tried 1.8 and 1.9 as well)
jackson-mapper-asl-1.4.2 (** I have tried 1.8 and 1.9 as well)


web.xml:



<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">

<display-name>My App</display-name>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

<session-config>
<session-timeout>5</session-timeout>
</session-config>

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/myApp-servlet.xml</param-value>
</context-param>

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterPro xy</filter-class>
</filter>

<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
<servlet-name>myApp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListe ner</listener-class>
</listener>

<servlet-mapping>
<servlet-name>myApp</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

</web-app>


Here is myApp-servlet.xml




<context:annotation-config />

<aop:aspectj-autoproxy proxy-target-class="true" />

<context:component-scan base-package="com.app" />

<mvc:annotation-driven />

<!-- Configures Handler Interceptors -->
<mvc:interceptors>
<!-- Changes the locale when a 'locale' request parameter is sent; e.g. /?locale=de -->
<bean class="org.springframework.web.servlet.i18n.LocaleChangeI nterceptor" />
</mvc:interceptors>

<!-- ... some mvc:view-controller declarations ... -->

<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewR esolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/pages/" />
<property name="suffix" value=".jsp" />
</bean>
...
<!-- For JSON converter support -->
<bean class="org.springframework.web.servlet.mvc.annotation.Ann otationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<!-- Support JSON -->
<bean class="org.springframework.http.converter.json.MappingJac ksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonDateMapper" />
</bean>
</property>
</bean>

<bean id="jacksonDateMapper" class="com.app.mapper.DateMapper">
<property name="mask" value="dd-MM-yyyy HH:mm" />
</bean>
...


And finally my DateMapper implementation:



package com.app.mapper;

import java.text.SimpleDateFormat;

import javax.annotation.PostConstruct;

import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.springframework.stereotype.Component;

@Component("jacksonDateMapper")
public class DateMapper extends ObjectMapper {

private String mask = "MM dd yyyy HH mm ss";

@PostConstruct
public void afterPropertiesSet() throws Exception {
super.configure(SerializationConfig.Feature.WRITE_ DATES_AS_TIMESTAMPS, false);
//Using Jackson > 1.8 it's possible to invoke 'getSerializationConfig().withDateFormat(new SimpleDateFormat(mask));', doesnt work ;(
getSerializationConfig().setDateFormat(new SimpleDateFormat(mask));
}

public void setMask(String mask) {
this.mask = mask;
}

}


In the end, the date objects are still serialized as numbers, not following my formatter. That's it, can someone point some direction?

Thanks a lot,
Iktuz.

iktuz
Oct 13th, 2011, 06:02 PM
Reading Scott Fredericks's Blog (http://scottfrederick.blogspot.com/2011/03/customizing-spring-3-mvcannotation.html) I realized that I should bind the original Spring's AnnotationMethodHandlerAdapter instance with my customized ObjectMapper. In the first approach I was creating two instances and of course, mine wasn't be used, what explains why nothing happened. So the final configuration must be done as follows:



<bean id="jacksonDateMapper" class="com.app.web.DateMapper">
<property name="mask" value="dd-MM-yyyy HH:mm" />
</bean>

<bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJac ksonHttpMessageConverter">
<property name="objectMapper" ref="jacksonDateMapper" />
</bean>

<bean class="com.app.web.ConverterRegister">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
</list>
</property>
</bean>


The class com.app.web.ConverterRegister is the binder:



package com.app.web;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autow ired;
import org.springframework.http.converter.HttpMessageConv erter;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.annotation.Ann otationMethodHandlerAdapter;

@Component
public class ConverterRegister {

@Autowired
private AnnotationMethodHandlerAdapter adapter;

private HttpMessageConverter<?>[] messageConverters;

public void setMessageConverters(HttpMessageConverter<?>[] messageConverters) {
this.messageConverters = messageConverters;
}

@PostConstruct
public void bindMessageConverters() {
adapter.setMessageConverters(messageConverters);
}

}


The class DateMapper does the job, applying the format:



package com.app.web;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.annotation.PostConstruct;

import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.map.ser.CustomSerializerFacto ry;
import org.springframework.stereotype.Component;

@Component("jacksonDateMapper")
public class DateMapper extends ObjectMapper {

private String mask = "MM-dd-yyyy HH:mm:ss";

@PostConstruct
public void afterPropertiesSet() throws Exception {
super.configure(SerializationConfig.Feature.WRITE_ DATES_AS_TIMESTAMPS, false);

//this one doesn't work at all, it's necessary to create and register a factory
//getSerializationConfig().withDateFormat(new SimpleDateFormat(mask));

//I am using Jackson 1.9 asl
CustomSerializerFactory factory = new CustomSerializerFactory();
factory.addSpecificMapping(Date.class, new JsonSerializer<Date>() {

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

@Override
public void serialize(Date value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {

jgen.writeString(new SimpleDateFormat(mask).format(value));
}});
this.setSerializerFactory(factory);
}

public void setMask(String mask) {
this.mask = mask;
}

}


The factory exemple above I saw in another blog, sorry for the guy with the due credits. I apologize not having the URL. Well, in the end all the date instances are converted based on the specified format.

In the future I intend to use the user's locale to switch among many masks, any help will be very appreciated.

Thanks a lot,
Iktuz.