14

I'm working on a REST API in Spring MVC 3.2RC1.

I'm fetching a JPA entity with a org.joda.time.DateTime timestamp in it and let Spring serialise it into JSON using

@RequestMapping(value = "/foobar", method = RequestMethod.GET, produces = "application/json")
@ResponseBody

Using the default Jackson2 settings in Spring as I've only added

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.1.1</version>
</dependency>

to my POM and let Spring wire it up itself.

The controller is generating:

"created":{"year":2012,"dayOfMonth":30,"dayOfWeek":5,"era":1,"dayOfYear":335,"weekOfWeekyear":48,"weekyear":2012,"monthOfYear":11,"yearOfEra":2012,"yearOfCentury":12,"centuryOfEra":20,"millisOfSecond":39,"millisOfDay":52684039,"secondOfMinute":4,"secondOfDay":52684,"minuteOfHour":38,"minuteOfDay":878,"hourOfDay":14,"millis":1354282684039,"zone":{"uncachedZone":{"cachable":true,"fixed":false,"id":"Europe/Stockholm"},"fixed":false,"id":"Europe/Stockholm"},"chronology":{"zone":{"uncachedZone":{"cachable":true,"fixed":false,"id":"Europe/Stockholm"},"fixed":false,"id":"Europe/Stockholm"}},"afterNow":false,"beforeNow":true,"equalNow":false}

But I would like it to be and ISO8601 date such as 2007-11-16T20:14:06.3Z (or with the offset).

My guess is that I need to access the ObjectMapper and set mapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); But how do I get access to the ObjectMapper when using

<mvc:annotation-driven />

P.S. I'm persisting the objects to PostgreSQL with JPA/Hibernate4 using UserType to get JodaTime support. D.S.

Update

The config below solves it for java.util.Date but still no dice for JodaTime.

<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">
                <beans:bean
                    class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
                    p:indentOutput="true" p:simpleDateFormat="yyyy-MM-dd'T'HH:mm:ss.SSSZ">
                </beans:bean>
            </beans:property>
        </beans:bean>
    </message-converters>
</annotation-driven>
NA.
  • 6,451
  • 9
  • 36
  • 36
  • 1
    http://stackoverflow.com/questions/9038005/spring-3-1-json-date-format – Kevin Bowersox Dec 04 '12 at 10:37
  • No, not really. I abondoned the Joda time type and went with a regular Date in my DTO. If you my memory serves me correct add inside the Jackson2ObjectMapperFactoryBean bean you get it as a epoch timestamp and the joda jackson module needs a patch to use the simpleDateFormat. – NA. Dec 20 '12 at 12:15

5 Answers5

13

Or, if you prefer doing your configuration in Java, it could look like this:

@Configuration
@EnableWebMvc
public class RestConfig extends WebMvcConfigurerAdapter {

    private ObjectMapper objectMapper() {
        Jackson2ObjectMapperFactoryBean bean = new Jackson2ObjectMapperFactoryBean();
        bean.setIndentOutput(true);
        bean.setSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        bean.afterPropertiesSet();
        ObjectMapper objectMapper = bean.getObject();
        objectMapper.registerModule(new JodaModule());
        return objectMapper;
    }

    private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(objectMapper());
        return converter;
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(mappingJackson2HttpMessageConverter());
    }

}
Claus Nielsen
  • 334
  • 3
  • 8
  • No need to explicitly register `JodaModule`. `Jackson2ObjectMapperFactoryBean` uses `Jackson2ObjectMapperBuilder` which registers the module automatically if it's available on the classpath. – Marcel Stör May 15 '15 at 08:06
11

I eventually got it working using jackson-datatype-joda:

Add another Maven dependency (match your Jackson version number):

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-joda</artifactId>
    <version>${jackson.version}</version>
</dependency>

Then register the JodaModule (which handles the conversion) to Jackson's ObjectMapper (here done in Spring, but you could create a helper class):

<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
    p:targetObject-ref="objectMapper" p:targetMethod="registerModule">
    <property name="arguments">
        <list><bean class="com.fasterxml.jackson.datatype.joda.JodaModule"/></list>
    </property>
</bean>

(You'll need to give the ObjectMapper an id so that it can be referenced in this way).

The Hibernate module is also registered in this way: https://github.com/FasterXML/jackson-module-hibernate

Note that you need to set a (Simple)DateFormat as shown in the question, but disabling SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS didn't seem to make any difference.

paulcm
  • 1,704
  • 1
  • 17
  • 17
  • Note that if you're using GAE (Google App Engine) they repackage some classes, one being `org.joda.time.LocalDate` into `com.google.appengine.repackaged.org.joda.time.LocalDate`, which, if you're using Google's repackaged classes, will not work. (+1 for `JodaModule` though!) – JJ Zabkar Oct 23 '13 at 22:48
  • You can also call *JodaMapper* straight away, e.g.: `private static final JodaMapper mapper = (JodaMapper) new JodaMapper().setPropertyNamingStrategy(new UpperCamelCaseStrategy());` – Jose Alban Jan 20 '16 at 16:45
  • This doesn't work in Junit test, the date serialized is a timestamp during the test, whiile a ISO format in app life cycle – Dimitri Kopriwa Aug 17 '16 at 16:04
11

Just to sum up answers and post working solution for JodaTime serialization in Spring (tested on Spring 3.2)

spring-context.xml

<bean id="objectMapper"
    class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean"
    p:indentOutput="true" p:simpleDateFormat="yyyy-MM-dd'T'HH:mm:ss.SSSZ">
</bean>
<bean
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
    p:targetObject-ref="objectMapper" p:targetMethod="registerModule">
    <property name="arguments">
        <list>
            <bean class="com.fasterxml.jackson.datatype.joda.JodaModule" />
        </list>
    </property>
</bean>

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
        <bean
            class="org.springframework.http.converter.ResourceHttpMessageConverter" />

        <bean
            class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper" ref="objectMapper" />
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

Maven dependencies (com.fasterxml.jackson-version is 2.1.1)

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>${com.fasterxml.jackson-version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${com.fasterxml.jackson-version}</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-joda</artifactId>
    <version>${com.fasterxml.jackson-version}</version>
</dependency>

After this your JodaTime fields in ResponseBody will be automatically serialized in JSON as "createdDate" : "2013-01-18T15:15:36.365+02:00"

  • 1
    The other option is to configure global date&time format [official documentation](http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/validation.html#format-configuring-formatting-globaldatetimeformat) – Dmytro Polivenok Feb 03 '13 at 16:50
  • 1
    I have tried a few different solutions, this one is great as you don't have to write any code and works perfectly. – bobmarksie Mar 04 '14 at 11:12
  • If you use `Jackson2ObjectMapperFactoryBean` you don't need to register `JodaModule` explicitly (what you do using `MethodInvokingFactoryBean`). `Jackson2ObjectMapperFactoryBean` uses `Jackson2ObjectMapperBuilder` which registers the module automatically if it's available on the classpath. – Marcel Stör May 15 '15 at 07:05
2

I was struggling with exactly the same b@#$d created by jackson from entity field with joda DayTime:

modifiedOn": {

"year": 2013,
"dayOfWeek": 6,
"era": 1,
"dayOfYear": 124,
"dayOfMonth": 4,
"weekOfWeekyear": 18,
"monthOfYear": 5,
"yearOfCentury": 13,
"centuryOfEra": 20,
"millisOfSecond": 0,
"millisOfDay": 81801000,
"secondOfMinute": 21,
"secondOfDay": 81801,
"minuteOfHour": 43,
"minuteOfDay": 1363,
"weekyear": 2013,
"yearOfEra": 2013,
"hourOfDay": 22,
"millis": 1367700201000,
"zone": {
    "uncachedZone": {
        "cachable": true,
        "fixed": false,
        "id": "Europe/Belgrade"
    },
    "fixed": false,
    "id": "Europe/Belgrade"
},
"chronology": {
    "zone": {
        "uncachedZone": {
            "cachable": true,
            "fixed": false,
            "id": "Europe/Belgrade"
        },
        "fixed": false,
        "id": "Europe/Belgrade"
    }
},
"afterNow": false,
"beforeNow": true,
"equalNow": false
}

As is mentioned here https://github.com/FasterXML/jackson-datatype-joda it is very easy with Jackson 2.0 but for newbie like me it was quite hard to figure out how to make it works, but finally found working piece of code:

Dependency in maven - that was simple

<dependency>
 <groupId>com.fasterxml.jackson.datatype</groupId>
 <artifactId>jackson-datatype-joda</artifactId>
 <version>2.1.2</version>
</dependency>

and some code from FasterXML documentation

objectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());

... but how to implement it? Here is the example - new class:

public class JodaObjectMapper extends ObjectMapper {

    public JodaObjectMapper() {
        super();
        registerModule(new JodaModule());
    }
}

And the last step - addition to spring.xml

<mvc:annotation-driven>
 <mvc:message-converters register-defaults="true">
 <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
 <property name="objectMapper">
 <bean class="net.jvw.JodaObjectMapper"/>
 </property>
 </bean>
 </mvc:message-converters>
</mvc:annotation-driven>

So now let look on produced json

"modifiedOn": 1367701129075

finally something easy to deal with

From blog post http://vanwilgenburg.wordpress.com/2013/01/14/return-usable-joda-dates-in-json-with-jackson/

If somebody want to see more code of my entity class or controller - please ask in comment and I will add sufficient code to this answer.

pbaranski
  • 22,778
  • 19
  • 100
  • 117
0

I have been struggling with the same issue, and have tried to simplify the Spring serialisation of DateTime to JSON this way in app-servlet.xml:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <property name="objectMapper">
                <bean class="no.bouvet.jsonclient.spring.JsonClientJackson2ObjectMapperFactoryBean"/>
            </property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

Here you can find the no.bouvet.jsonclient.spring.JsonClientJackson2ObjectMapperFactoryBean from the java-json-client library

Jan-Terje Sørensen
  • 14,468
  • 8
  • 37
  • 37