1

I am facing one very known issue yet no solution is working for me. Any quick help will be grateful.

From MVC controller, I am calling REST service which returns me Model object with LocalDate as one of the fields. JacksonJAXbJSONProvider does not parse the Localdate object. I wrote custom Mapper as below And dependency is added in pom.xml. Other required dependencies are also added(jackson databind, core, annotation)

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.6.1</version>
</dependency>

Custom ObjectMapper class :

public class LocalDateObjectMapperContextResolver extends ObjectMapper{
    @Provider
    public LocalDateObjectMapperContextResolver() {

       registerModule(new JavaTimeModule());
       //findAndRegisterModules();
       configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

}

This was suggested in Java 8 LocalDate Jackson format

My spring.xml(contains configuration for MVC controller etc...) has already one mapper configured which does conversion automatically as below

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" 
            p:objectMapper-ref="myobjectMapper"/>
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" 
            p:objectMapper-ref="objectMapper"/>
    </mvc:message-converters>
</mvc:annotation-driven>

<bean id="objectMapper" class="org.codehaus.jackson.map.ObjectMapper"/>
<bean id="myobjectMapper" class="mypackage.LocalDateObjectMapperContextResolver"/>

Below is stub file which connects to REST Service. I also tried to provide provider as below after above didnot work, but no luck here too.

<jaxrs:client id="testclient"
    serviceClass="package1.RESTService"
    username="abc"
    password="abc"
    address="$serviceURL">

    <jaxrs:features>
        <bean class="org.apache.cxf.transport.common.gzip.GZIPFeature"/>
        <cxf:logging/>
    </jaxrs:features>

    <jaxrs:providers>
        <bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
        <bean class="mypackage.LocalDateObjectMapperContextResolver"/>
    </jaxrs:providers>

</jaxrs:client> 

Now How do I configure my Context resolver which I just wrote so that I should be able to convert LocalDate.

What Am I doing wrong ?

I am getting below exception

No suitable constructor found for type [simple type, class java.time.LocalDate]: can not instantiate from JSON object (need to add/enable type information?)
Community
  • 1
  • 1
curious_soul
  • 559
  • 1
  • 8
  • 29
  • Why do you declare botth custom and original ObjectMapper in your Spting context? Your custom already extends ObjectMapper so you just need to declare your custom object mapper. It's possible that Spring is getting mixed up when choosing a message converter and use the wrong object mapper. – Matt Jun 28 '16 at 18:37
  • Thanks for reply Matt, I agree with your argument. But I did it for reason, I am working on enhancement to existing application where lot of other mvc controllers use default one(from 'codehaus'), The one I am extending is from 'fasterxml' (which has 'LocalDate' support) and I pass it to 'MappingJackson2HttpMessageConverter' Let me try just for testingview to see if it works – curious_soul Jun 28 '16 at 19:40
  • Ok, so you're using 2 different versions of Jackson at the same time? It's generally a bad practice, even though classes are in different packages. You should try removing the old version and the other ObjectMapper just to clear this out. – Matt Jun 28 '16 at 20:54
  • I tried after removing the old one, I still see the same issue. Am I registering it wrong side?. I mean Should it be server side(REST service which is generating JSON response) or client side where I am reading the response. I also tried registering CustomContextResolver annotated with provider both server side and client side but both of them did not work. getContext() method of resolver never gets called during conversion. Any idea why ? – curious_soul Jul 02 '16 at 16:07
  • Its solved. Here is my code. Hope it helps some one. http://www.coderanch.com/t/667353/tools/Java-LocalDate-Jackson-format – curious_soul Jul 05 '16 at 07:03
  • Ok great, now you can post an answer to your own question and explain what was wrong and how you fixed it instead of providing just a link. – Matt Jul 05 '16 at 09:48
  • Due to word limit in comments, I coudn't post the code here, All I did was extend the ObjectMapper and register a new Module. That module will have serializers and deserializers for LocalDate type. Finally pass this ObjectMapper as first argument and default annotations to JacksonProvider used in server side and client side. Code is already posted in above link> Thanks for help. Cheers ! – curious_soul Jul 05 '16 at 11:06
  • Ok, what I was just suggesting is that you post a full answer (not a comment) and mark it as the accepted answer so next time someone look at your problem, they will find the solution straight and not have to look into the comments. – Matt Jul 05 '16 at 11:09
  • OK understood, Thanks. Will post it – curious_soul Jul 05 '16 at 13:53
  • Done, Here it is but I dont know how to make it accepted solution http://stackoverflow.com/questions/38209571/java-8-localdate-conversion-to-from-json-with-jackson-in-cxf-rest-application – curious_soul Jul 05 '16 at 18:24
  • Actually, you posted another question and not an answer to your question (this page). – Matt Jul 05 '16 at 21:25
  • Good, now you can close the question you accidently opened. And mark your answer as the accepted one. – Matt Jul 06 '16 at 12:10

1 Answers1

2

I solved it. Extended ObjectMapper, created new module, added serializer and deserialzer to it and finally registered module to Objectmapper and passed objectmapper as first argument in JacksonJaxbJsonProvider constructor, 2nd argument passed is default annotations. Below is my code.

    public class MyObjectMapper extends ObjectMapper {

public MyObjectMapper() {
    SimpleModule sm = new SimpleModule("SomeName", new Version(1,1,1,""));
    sm.addSerializer(LocalDate.class, new JsonSerializer<LocalDate>() {
        @Override
        public void serialize(LocalDate value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
            ToStringSerializer.instance.serialize(value, jgen, provider);
        }
    });
    sm.addSerializer(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
        @Override
        public void serialize(LocalDateTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
            ToStringSerializer.instance.serialize(value, jgen, provider);
       }
    });
    sm.addDeserializer(LocalDate.class, new JsonDeserializer<LocalDate>() {
        @Override
        public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            return LocalDate.parse(jp.getText());
        }
    });
    sm.addDeserializer(LocalDateTime.class, new JsonDeserializer<LocalDateTime>() {
        @Override
        public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            return LocalDateTime.parse(jp.getText());
        }
    });
    registerModule(sm);
}

Server Side configuration:

    <bean id="mapper" class="mymapperpkg.MyObjectMapper"/>

<jaxrs:server>
......
.....
<jaxrs:serviceBeans>....</jaxrs:serviceBeans>
......
......

<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider">
                <constructor-arg index="0" ref="mapper"/>
                <constructor-arg index="1">
                    <util:constant static-field="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS"/>
                </constructor-arg>
            </bean> -->

</jaxrs:server>

Client side configarion

   <bean id="mapper" class="mymapperpkg.MyObjectMapper"/>

 <jaxrs:client id="deliveryItemClient"
                 serviceClass="servicepkg.YourRESTServiceClass"
                 username="adadsad"
                 password="asdasdasd"
                 address="adresss to deployed service">


        <jaxrs:providers>
            <bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider">
                <constructor-arg index="0" ref="mapper"/>
                <constructor-arg index="1">
                    <util:constant static-field="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS"/>
                </constructor-arg>
            </bean>
        </jaxrs:providers>

    </jaxrs:client>

This is when REST application is generating LocalDate.

If REST application is consuming LocalDate, you can write ParamHanlder to parse json string back to LocalDate like below and register this under providers in jaxrs:server

MyCustomParamHandler implements Paramhandler<Localdate>{

LocalDate fromString(String jsonString){
          return LocalDate.parse(jsonString);
    }
}

Cheers!

curious_soul
  • 559
  • 1
  • 8
  • 29