16

Problem using JAX-RS and java.time.LocalDate (java8).

I want to pass an object like this into a JAX-RS method using JSON:

Person {
  java.time.LocalDate birthDay;
}

The exception I get is:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class java.time.LocalDate]: can not instantiate from JSON object (need to add/enable type information?) at [Source: io.undertow.servlet.spec.ServletInputStreamImpl@21cca2c1; line: 2, column: 3]

How can I create some kind of an interceptor that maps json-dates to java.time.LocalDate? I have tried implemented a MessageBodyReader, but if the LocalDate is a field in another class, I have to write a MessageBodyReader for each class holding a LocalDate (as far as I've understood).

(Java EE7 (only using the javaee-api and don't want any third party dependencies), JAX-RS, Java 8, Wildfly 8.2)

Any suggestions?

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
jorgen.ringen
  • 1,285
  • 5
  • 14
  • 20

1 Answers1

29

Normally I'd say to write a Serializer/Deserializer for Jackson, but since you don't want any other dependencies, you can use a JAXB solutions. Jackson (with Resteasy) comes with support for JAXB annotations. So what we can do is just write an XmlAdapter to convert from the String to the LocalDate. An example would be something like

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class LocalDateAdapter extends XmlAdapter<String, LocalDate> {

    @Override
    public LocalDate unmarshal(String dateString) throws Exception {
        return LocalDate.parse(dateString, DateTimeFormatter.ISO_DATE);
    }

    @Override
    public String marshal(LocalDate localDate) throws Exception {
        return DateTimeFormatter.ISO_DATE.format(localDate);
    }
}

You can choose any formatting you want, I just used the DateTimeFormatter.ISO_DATE, which will basically look for this format (2011-12-03).

Then all you need to do is annotate the field for getter of the type

public class Person {
    private LocalDate birthDate;

    @XmlJavaTypeAdapter(LocalDateAdapter.class)
    public LocalDate getBirthDate() { return birthDate; }

    public void setBirthDate(LocalDate birthDate) { 
        this.birthDate = birthDate;
    }
}

If you don't want to clutter your model classes with this annotation, then you can simply declare the annotation at the package level.

In a package-info.java file in the same package as the model class(es), add this

@XmlJavaTypeAdapters({
    @XmlJavaTypeAdapter(type = LocalDate.class, 
                        value = LocalDateAdapter.class)
})
package thepackage.of.the.models;

import java.time.LocalDate;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;

Test

@Path("/date")
public class DateResource {
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response postPerson(Person person) {
        return Response.ok(DateTimeFormatter.ISO_DATE.format(
                           person.getBirthDate())).build();
    }
}

@Test
public void testResteasy() throws Exception {
    WebTarget target = client.target(
            TestPortProvider.generateURL(BASE_URI)).path("date");
    String person = "{\"birthDate\":\"2015-01-04\"}";
    Response response = target.request().post(Entity.json(person));
    System.out.println(response.readEntity(String.class));
    response.close();
}

Result : 2015-01-04


UPDATE

Also for Jackson (I know the OP said without dependencies, but this is for others), you can use the jackson-datatype-jsr310 module. See full solution here

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thank you! It works, but I actually thought there was a simpler way to just create a generic String/LocalDate converter in plain JAX-RS :) – jorgen.ringen Jan 05 '15 at 19:17
  • 1
    There is, but not in this context. With JSON to POJO (vice-versa), Jackson will be doing the unmarshalling/marshalling. So we need to tap into Jackson. The context which does allow us to create converters, is say for a `@QueryParam` or `@FormaParam` or `@PathParam` string to pojo conversion, but that is not the case here :-) – Paul Samsotha Jan 06 '15 at 04:15
  • 1
    See [this](http://stackoverflow.com/a/32677962/2587435) for param conversion with a `ParamConverter`. This is for `@QueryParam`, `@FormParam`, and other `@XxxParam`s – Paul Samsotha Oct 02 '15 at 09:47