javajerseyjackson2jersey-test-framework

Jackson LocalDate/Time deserialization


I configured jackson so that it gives me a smiple string representation if java.time.LocalDate and java.time.LocalDateTime. This works find in the serialization process, e.g when I get data on the REST api.

It doesn't work the other way round though. When I try to send data to the server and the JSON should be parsed to java objects this exception is thrown:

javax.ws.rs.client.ResponseProcessingException: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.time.LocalDateTime: no String-argument constructor/factory method to deserialize from String value ('2018-04-19T14:10:30.903')

After a few hours of research I managed to get it to work, but only with the attributes I annotated with @JsonDeserialize(using = LocalDateDeserializer.class) or @JsonDeserialize(using = LocalDateTimeDeserializer.class) respectively.

In my opinion it would be ideal, if I could define these mappings in one central place.

ObjectMapper configuration:

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
    @Override
    public ObjectMapper getContext(Class<?> aClass) {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

        return mapper;
    }
}

I tried to add custom deserializers to the JavaTimeModule, but without success:

JavaTimeModule dateTimeModule = new JavaTimeModule();
dateTimeModule.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE);
dateTimeModule.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);

mapper.registerModule(dateTimeModule);

Long story short: Is there a way to define the mapping globally, so that I do not need these annotations on every field. Thanks!

EDIT:

Alright: I tested it with postman and without the annotations and it worked as expected. However, when I run the unit test (JerseyTest) it throws the mentioned exception. I register the ObjectMapperContextResolver to the test application, but probably I am missing something.

Sorry about not mentioning that I was in the unit tests.

TestClass:

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;

import javax.ws.rs.core.Application;

import java.time.LocalDate;
import java.time.LocalDateTime;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;


public class PocRestTest extends JerseyTest {

    private static PocService mockedPocService;

    @Override
    protected Application configure() {
        mockedPocService = Mockito.mock(PocService.class);
        ResourceConfig config = new ResourceConfig();
        config.register(new PocRest(mockedPocService));
        config.register(ObjectMapperContextResolver.class);
        return config;
    }

    private Poc dto;

    @Before
    public void init() {
        dto = new Poc();
        dto.setId(1);
        dto.setName("hi rest");
        dto.setDate(LocalDate.now());
        dto.setDateTime(LocalDateTime.now());

        doReturn(dto).when(mockedPocService).getPocById(1);
    }

    @Test
    public void test() {
        Poc response = target("poc/1").request().get(Poc.class);
        assertEquals(dto.getId(), response.getId());
        assertEquals(dto.getName(), response.getName());
        assertEquals(dto.getDate(), response.getDate());
        assertEquals(dto.getDateTime(), response.getDateTime());

        verify(mockedPocService).getPocById(1);
        verifyNoMoreInteractions(mockedPocService);
    }
}

Solution

  • You registered the ContextResolver with the server via the configure() method of the JerseyTest, but if you look at the exception, you'll see that is a client side exception (notice the client in the package name). So the problem is on the client side. What you are missing is that the deserialization also needs to happen on the client side from JSON to Poc, so you also need the ContextResolver registered on the client. To do that, you can override configureClient() on the JerseyTest and register the ContextResolver there.

    @Override
    public void configureClient(ClientConfig config) {
        config.register(ObjectMapperContextResolver.class);
    }