Spring MVC‘s message conversion feature is the bomb. I love it; I wish I’d started using it long ago. Just make sure your JSON fields match your POJO property names, and your MVC controller includes a POJO parameter or return value. Then Spring MVC auto-converts JSON to and from your POJO!
Spring MVC uses the Jackson JSON library. It can use either Jackson 1 or Jackson 2, whichever you add to the webapp classpath.
By default, Jackson marshals dates following an epoch timestamp strategy, specifically, the number of milliseconds since January 1, 1970 UTC. In other words, a very long number like this: 1399908056241. Not very nice for our users, and we’d rather not have to write custom date parsing and formatting code.
So we have to configure Spring MVC and JSON to use a different format. The above-linked article is pretty clear on how to configure Jackson in Java:
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
But Spring MVC never exposes Jackson via Java code; it just works, no programmer intervention needed. So we need a way to configure the Jackson object mapper in Spring configuration. I added this XML to my Spring MVC configuration file:
<!-- set JSON date format to ISO-8601 e.g. 1970-01-01T00:00:00.000+0000 --> <bean id="sourceObjectMapper" class="com.fasterxml.jackson.databind.ObjectMapper"/> <bean id="acmObjectMapper" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="targetObject" ref="sourceObjectMapper"/> <property name="targetMethod" value="disable"/> <property name="arguments" value="WRITE_DATES_AS_TIMESTAMPS"/> </bean> <bean id="acmJacksonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper" ref="acmObjectMapper"/> </bean>
The sourceObjectMapper is a template. The acmObjectMapper applies the disable(WRITE_DATES_AS_TIMESTAMPS) method to the template object mapper, which returns another object mapper. The acmJacksonConverter is a Spring MVC message converter, configured to use the ISO-8601 date format.
All we need to do now is tell Spring MVC to use our nice new message converter. I’m using the mvc:annotation-driven approach to configuring Spring MVC. The mvc namespace allows you to specify a list of message converters:
<mvc:annotation-driven> <mvc:message-converters> <!-- We configure the Jackson mapper to output dates in ISO801 format. This requires adding our customized Jackson mapper to the list of Spring MVC message converters. But, if we just add our bean here all by itself, it will handle requests it should not handle, e.g. encoding strings. So we need to add the other standard message converters here too, and make sure to put the customized Jackson converter AFTER the string converter. --> <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/> <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter"/> <bean class="org.springframework.http.converter.StringHttpMessageConverter"/> <bean class="org.springframework.http.converter.ResourceHttpMessageConverter"/> <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/> <ref bean="acmJacksonConverter"/> <bean class="org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter"/> <!-- atom feed requires com.sun.syndication package ... --> <!--<bean class="org.springframework.http.converter.feed.AtomFeedHttpMessageConverter"/>--> <bean class="org.springframework.http.converter.BufferedImageHttpMessageConverter"/> <bean class="org.springframework.http.converter.FormHttpMessageConverter"/> <bean class="org.springframework.http.converter.xml.Jaxb2CollectionHttpMessageConverter"/> <!-- marshalling converter requires spring oxm --> <!--<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"/>--> </mvc:message-converters> </mvc:annotation-driven>
As you can see our custom converter is 6th in the list, after many built-in converters. When I include only the custom converter, the custom converter gets first crack at each request, and it will try to handle requests it shouldn’t! So I ended up including all the standard converters, along with the custom one.
This setup took me a few hours to figure out. But now JSON dates are represented in a nicely readable standard format, e.g. 1970-01-01T00:00:00.000+0000, with no further programming ever required on my part.
0 Comments