How do I (un)marshall a Date as a time-stamp with jackson

Anders picture Anders · Feb 11, 2015 · Viewed 8.3k times · Source

I'm having trouble (un)marshalling java.util.Date objects into timestamps. Ideally the timestamps should be in a UTC-0 format and not the server's local time zone. Although I can work around that pretty easily if I need to.

NB: I am aware that here are several similar topics on stack overflow but everyone I have come across is either outdated (with respect to the API's being used) or are related to serializing Date objects to strings.

Here is an excerpt of my POM file:

<dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.0.1</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.15</version>
</dependency>

Sample model class:

public class PersonJSON {
    private Date birthDate;
}

Expected output (assuming birth date is 01 Feb 2015 00:00:00 UTC-0):

{"birthDate":1422748800000}

Current output:

{"birthDate":"2015-02-01"}

Is it possible to fix this (ideally using an annotation)?

Solution

As it turns out the error was caused by an implicit conversion from Java.sql.Date to Java.util.Date. The conversion throws no exceptions and the sql.Date extends util.Date so logically it should work, but it doesn't. The solution was to extract the timestamp from the Java.sql.Date and use it as input to the Java.util.Date constructor.

If you do want to alter the format the approaches listed by peeskillet is correct assuming you have a valid Java.util.Date object.

Answer

Paul Samsotha picture Paul Samsotha · Feb 11, 2015

You just need to configure the SerializationFeature.WRITE_DATES_AS_TIMESTAMPS on the ObjectMapper. From what I tested I didn't need to configure the deserialization, it worked fine passing a timestamp.

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {

    private final ObjectMapper mapper;

    public ObjectMapperContextResolver() {
        mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
    }

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

}

As far as setting this feature by annotation per field, I am not sure how to do that with Jackson, without writing a custom serializer (but I wouldn't doubt if there is some other way out there). You can always use the JAXB annotation support that comes with the Jackson provider. Just write an XmlAdapter and annotate the field with @XmlJavaTypeAdatper(YourDateAdapter.class). Here's an example


UPDATE

Complete example.

Required dependencies

<dependency>
    <groupId>org.glassfish.jersey.test-framework.providers</groupId>
    <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
    <version>2.15</version>
</dependency>
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.15</version>
</dependency>

Test (you need to register the context resolver from above)

import java.util.Date;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import jersey.stackoverflow.provider.ObjectMapperContextResolver;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

public class ObjectMapperTest extends JerseyTest {

    public static class Person {
        public Date birthDate;
    }

    @Path("/person") @Produces(MediaType.APPLICATION_JSON)
    public static class PersonResource {
        @GET 
        public Response getPerson() {
            Person person = new Person();
            person.birthDate = new Date();
            return Response.ok(person).build();
        }
    }

    @Override
    public Application configure() {
        return new ResourceConfig(PersonResource.class,
                                  ObjectMapperContextResolver.class);
    }

    @Test
    public void test() {
        String personJson = target("person").request().get(String.class);
        System.out.println(personJson);
    }
}

Result: {"birthDate":1423738762437}


Update 2

I don't think this answer is correct. It seems Jackson already serializes as timestamps by default. If we didn't want timestamps, then we would set the above configuration as false. But this still does not answer the OPs question. Not sure what the problem is.