Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jackson2 JSON ISO 8601 date from JodaTime in Spring 3.2RC1

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>
like image 719
NA. Avatar asked Dec 04 '12 10:12

NA.


4 Answers

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());
    }

}
like image 187
Claus Nielsen Avatar answered Nov 07 '22 20:11

Claus Nielsen


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.

like image 32
paulcm Avatar answered Nov 07 '22 19:11

paulcm


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"

like image 40
Dmytro Polivenok Avatar answered Nov 07 '22 20:11

Dmytro Polivenok


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.

like image 2
pbaranski Avatar answered Nov 07 '22 20:11

pbaranski