Results 1 to 2 of 2

Thread: Jackson MappingJacksonHttpMessageConverter/ObjectMapper for Date not working

  1. #1
    Join Date
    Feb 2010
    Posts
    6

    Default RESOLVED Jackson MappingJacksonHttpMessageConverter/ObjectMapper for Date not working

    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:

    HTML Code:
    <?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.DelegatingFilterProxy</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.ContextLoaderListener</listener-class>
    	</listener>
    
    	<servlet-mapping>
    		<servlet-name>myApp</servlet-name>
    		<url-pattern>*.html</url-pattern>
    	</servlet-mapping>
    
    </web-app>
    Here is myApp-servlet.xml

    HTML Code:
        <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.LocaleChangeInterceptor" />
        </mvc:interceptors>
    
        <!-- ... some mvc:view-controller declarations ... -->
    
        <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
            <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.AnnotationMethodHandlerAdapter">
            <property name="messageConverters">
                <list>
                    <!-- Support JSON --> 
                    <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
                        <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:

    Code:
    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.
    Last edited by iktuz; Oct 13th, 2011 at 06:05 PM. Reason: Title status has changed

  2. #2
    Join Date
    Feb 2010
    Posts
    6

    Default

    Reading Scott Fredericks's Blog 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:

    HTML Code:
        <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.MappingJacksonHttpMessageConverter">
            <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:

    Code:
    package com.app.web;
    
    import javax.annotation.PostConstruct;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
    
    @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:

    Code:
    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.CustomSerializerFactory;
    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.
    Last edited by iktuz; Oct 13th, 2011 at 06:14 PM. Reason: Fixed some mispelling

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •