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