Cannot deserialize java.time.Instant

Mircea Stanciu picture Mircea Stanciu · May 7, 2018 · Viewed 8.2k times · Source

I have a RestEasyClient that has to deserialize an object that has a java.time.Instant inside. I tried to register the new JavaTimeModule from jsr310 but still got errors:

    ObjectMapper mapper = new ObjectMapper()
            .registerModule(new JavaTimeModule());

    ResteasyClient client = new ResteasyClientBuilder()
            .register(mapper)
            .build();
    ResteasyWebTarget target = client.target(UriBuilder.fromPath(SERVICE_URL + "/api"));

Error:

Can not construct instance of java.time.Instant: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)

After modifying Rest Server to properly serialize the Instant class (ex: "fromTime": 1525681860)

New Error:

Can not construct instance of java.time.Instant: no double/Double-argument constructor/factory method to deserialize from Number value (1.52568186E9)

I managed to simulate this:

    ObjectMapper deserializer = new ObjectMapper()
            .registerModule(new JavaTimeModule());

    Instant probe = deserializer.readValue("1525681860", Instant.class);
    System.out.println(probe);

If I remove the "registerModule" line, I get the same error.

Therefore, the conclusion is that RestEasyClient not registering the module. I am definitely doing something wrong.

Answer

cassiomolin picture cassiomolin · May 7, 2018

You could define a ContextResolver for ObjectMapper:

public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {

    private final ObjectMapper mapper;

    public ObjectMapperContextResolver() {
        this.mapper = createObjectMapper();
    }

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

    private ObjectMapper createObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        return mapper;
    }
}

And then register the resolver in your client instance:

ResteasyClient client = new ResteasyClientBuilder()
    .register(ObjectMapperContextResolver.class).build();

Alternatively you could register an instance of JacksonJsonProvider. This class is the basic implementation of JAX-RS abstractions (MessageBodyReader and MessageBodyWriter) needed for binding JSON content to and from Java objects.

You can use the constructor that accepts an ObjectMapper instance.