JAX-RS и java.time.LocalDate в качестве входного параметра

Проблема с использованием JAX-RS и java.time.LocalDate (Java8).

Я хочу передать такой объект в метод JAX-RS, используя JSON:

Person {
  java.time.LocalDate birthDay;
}

Исключение, которое я получаю:

com.fasterxml.jackson.databind.JsonMappingException: Не найдено подходящего конструктора для типа [простой тип, класс java.time.LocalDate]: невозможно создать экземпляр объекта JSON (нужно добавить / включить информацию о типе?) в [Source: io.undertow.servlet.spec.ServletInputStreamImpl@21cca2c1; строка: 2, столбец: 3]

Как я могу создать какой-то перехватчик, который отображает JSON-даты java.time.LocalDate? Я пытался реализовать MessageBodyReader, но если LocalDate это поле в другом классе, я должен написать MessageBodyReader для каждого класса проведения LocalDate (насколько я понял).

(Java EE7 (используется только javaee-api и не требуется никаких сторонних зависимостей), JAX-RS, Java 8, Wildfly 8.2)

Какие-либо предложения?

1 ответ

Решение

Обычно я бы сказал написать сериализатор / десериализатор для Джексона, но, поскольку вам не нужны никакие другие зависимости, вы можете использовать решения JAXB. Джексон (с Resteasy) поставляется с поддержкой аннотаций JAXB. Итак, что мы можем сделать, это просто написать XmlAdapter преобразовать из строки в LocalDate, Примером будет что-то вроде

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

Вы можете выбрать любое форматирование, которое вы хотите, я просто использовал DateTimeFormatter.ISO_DATE, который в основном будет искать этот формат (2011-12-03).

Затем все, что вам нужно сделать, это аннотировать поле для получателя типа

public class Person {
    private LocalDate birthDate;

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

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

Если вы не хотите загромождать классы своей модели этой аннотацией, вы можете просто объявить аннотацию на уровне пакета.

В package-info.java файл в том же пакете, что и класс модели, добавьте

@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;

Тестовое задание

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

Результат: 2015-01-04


ОБНОВИТЬ

Также для Джексона (я знаю, что ОП говорит без зависимостей, но это для других), вы можете использовать модуль https://github.com/FasterXML/jackson-datatype-jsr310. Смотрите полное решение здесь

Другие вопросы по тегам