1

I'm building RESTful web services using Java EE 7 on GlassFish 4. When serializing POJOs containing java.util.Date objects, the timezone information is not included.

How can I customize object serialization so java.util.Dates have timezone info included?

For example, instead of this:

{
  "id": 1234,
  "foo": "2014-05-19T13:53:49.392"
}

I'd instead like this:

{
  "id": 1234,
  "foo": "2014-05-19T13:53:49.392+09:30"
}

Where the server's timezone is GMT + 09:30.

Steve
  • 8,066
  • 11
  • 70
  • 112
  • If you're using Jackson you can use [ObjectMapper.setDateFormat](http://fasterxml.github.io/jackson-databind/javadoc/2.1.0/com/fasterxml/jackson/databind/ObjectMapper.html#setDateFormat(java.text.DateFormat)) – lefloh May 19 '14 at 07:27
  • Yep, I switched to Jackson a moment ago and it works in responses, but now parsing fails on request bodies. – Steve May 19 '14 at 07:30
  • According to the Documentation this should work for serializing and deserializing. You could also try to use a custom [Deserializer](http://stackoverflow.com/a/5598277) and [Serializer](http://stackoverflow.com/a/10106998) – lefloh May 19 '14 at 07:40
  • Yeah, issue seems to be unparseable date exceptions. Apparently `yyyy-MM-dd'T'HH:mm:ss.SSSZ` cannot parse `2014-05-20T09:00:00.000+09:30`. Anyway, if I come to the entire solution before someone else, I'll post it as the answer. – Steve May 19 '14 at 07:53
  • I think the colon in your time-zone is the problem. Please take a look at the [SimpleDateFormat](http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html) – lefloh May 19 '14 at 08:57
  • Ah yes, that's it. OK, here comes the answer... – Steve May 20 '14 at 00:23

1 Answers1

6

This is caused by a bug in MOXy, which is the default JSON (de)serializer for JAX-RS in GlassFish.

The workaround is to switch JSON providers to Jackson.

The first step is to add these dependencies to the build:

<!-- Provides JacksonFeature -->
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.8</version>
    <scope>provided</scope>
</dependency>

<!-- Provides ObjectMapper and SerializationConfig -->
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.9.13</version>
    <type>jar</type>
</dependency>

Next step, add JacksonFeature as a provider, which will replace MOXy as the JSON serializer:

@ApplicationPath("/api/1")
public class ApplicationConfig extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> resources = new java.util.HashSet<>();
        addRestResourceClasses(resources);

        // Add Jackson feature.
        resources.add(org.glassfish.jersey.jackson.JacksonFeature.class);

        return resources;
    }

    private void addRestResourceClasses(Set<Class<?>> resources) {
        // JAX-RS resource classes added here - maintained by NetBeans.
    }
}

The default behaviour of Jackson is to represent java.util.Date in its milliseconds since the epoch form. To change that to ISO-8601:

import java.text.SimpleDateFormat;
import java.util.TimeZone;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;

@Provider
@Produces("application/json")
public class JacksonConfigurator implements ContextResolver<ObjectMapper> {

    public JacksonConfigurator() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
        mapper.setDateFormat(dateFormat);
        mapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

    @Override
    public ObjectMapper getContext(Class<?> clazz) {
        return mapper;
    }

    private final ObjectMapper mapper = new ObjectMapper();
}
Steve
  • 8,066
  • 11
  • 70
  • 112
  • 2
    Works great, except a typo in the format. The Z should be 'Z'. The format is: "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" – TalL Dec 29 '14 at 09:51