I have Java8 LocalDateTime in my Jax-RS REST APIs. My webapp is deployed in wildfly10. When i make a POST call(which includes LocalDateTime as parameter) i get following exception;
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class java.time.LocalDateTime] from String value ('2016-06-02T00:08:25.605Z'); no single-String constructor/factory method
at [Source: io.undertow.servlet.spec.ServletInputStreamImpl@396d1714; line: 2, column: 3] (through reference chain: com.leightonobrien.core.model.base.Company["created"])
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:843)
at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:277)
at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:284)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1150)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:153)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:144)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:523)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:101)
at com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap.findDeserializeAndSet(BeanPropertyMap.java:285)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:248)
Based on following guides ;
and
jaxrs could not find my custom (de)serializers for joda.money type
and
https://github.com/FasterXML/jackson-datatype-jsr310
I have written my provider and registered in the application path;
package com.test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;
import javax.ws.rs.ext.Provider;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
@Provider
public class LocalDateTimeConverterProvider extends JacksonJsonProvider implements ParamConverterProvider {
private final LocalDateTimeConverter converter = new LocalDateTimeConverter();
@Override
public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
if (!rawType.equals(LocalDateTime.class))
return null;
return (ParamConverter<T>) converter;
}
public class LocalDateTimeConverter implements ParamConverter<LocalDateTime> {
@Override
public LocalDateTime fromString(String value) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
LocalDateTime dateTime = LocalDateTime.parse(value, formatter);
return dateTime;
}
@Override
public String toString(LocalDateTime value) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
String formattedDateTime = value.format(formatter);
return formattedDateTime;
}
}
public LocalDateTimeConverterProvider() {
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.registerModule(new JavaTimeModule());
setMapper(mapper);
}
}
import javax.ws.rs.ApplicationPath;
@ApplicationPath("/rest")
public class RestApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>();
set.add(com.test.JsonMoneyProvider.class);
set.add(com.test.DurtaionConverterProvider.class);
set.add(com.test.LocalDateTimeConverterProvider.class);
set.add(com.test.MoneyConverterProvider.class);
...
I make POST call like;
curl -X POST --header 'Content-Type: application/json' -d '{
"created": "2016-06-02T00:08:25.605Z",
"updated": "2016-06-02T00:08:25.605Z",
"id": 0,
"code": "string",
"name": "string",
"abn": "string",
"addresses": [
{
"id": 0,
"address1": "string",
"address2": "string",
"city": "string",
"state": "string",
"postcode": "string",
"country": "string",
"gps": {
"latitude": {
"latitude": 0,
"value": 0
},
"longitude": {
"longitude": 0,
"value": 0
}' 'http://localhost:8080/test2dbwar/rest/Companys'
How can I overcome above issue? Now I'm clueless..I tried to put all stuff (try to avoid wildfly's jax-rs complex support issue, jackson serialization issue) and sort out the issue..
Any help?
The first question you linked to involves conversion of @XxxParam
annotations. This is what the ParamConverterProvider
is for. This is a completely different (de)serailization process from entity body (de)serialization.
For entity (de)serialization, MessageBodyReader/MessageBodyWriter
s are used. Jackson provides one such implementation, in its JacksonJsonProvider/JacksonJaxbJsonProvider
, which you are currently using, whether you know it or not. So the configuration for the LocalDataTime
needs to somehow be configured with that provider.
The only way to configure Jackson support for the LocalDateTime
serialization, is through it's ObjectMapper
. You can create a custom Json(De)Serializer
, as mentioned in this post (Option two), or you can use the JSR310Module
(that already has custom Json(De)Serializer
for LocalDateTime
), mentioned in this post.
To actually configure the JacksonJsonProvider/JacksonJaxbJsonProvider
to use your configured ObjectMapper
, you can use a ContextResolver
, as mentioned in both of the previous links, or you can construct the JacksonJsonProvider
with the ObjectMapper
, and register that provider
ObjectMapper mapper = new ObjectMapper();
mapper..configurModuleOrSerializer
JacksonJsonProvider provider = new JacksonJsonProvider(mapper);
registerProvier(provider)
Personally I would just go with the ContextResolver
, as mentioned in the links. The difference is that with the above code, the ObjectMapper
is provided to the provider explicitly, while with the ContextResolver
, during runtime, the provider will search the JAX-RS registry for a ContextResolver
of type ObjectMapper
, and then obtain it that way.