Ошибка десериализации LocalDateTime в запросе POST - PullRequest
0 голосов
/ 03 ноября 2018

У меня есть класс, который содержит два члена LocalDateTime:

public class Foo
{
    private final LocalDateTime start;
    private final LocalDateTime end;

    public Foo(LocalDateTime start, LocalDateTime end)
    {
        this.start = start;
        this.end = end;
    }
}

У меня есть контроллер Spring Boot для обработки запроса POST:

@PostMapping(value = "/my-resource")
public ResponseEntity<?> bar(@RequestBody Foo foo)
{
   return ResponseEntity.ok().build();
}

Если я отправлю запрос POST следующим образом:

curl -X POST \
  http://localhost:8080/my-resource \
  -H 'Content-Type: application/json' \
  -d '{
    "start":[2016, 1, 1, 10, 24],
    "end":[2016, 1, 1, 10, 24]
}
'

возвращается следующая ошибка:

"message": "Type definition error: [simple type, class Foo]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `Foo` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)\n at [Source: (PushbackInputStream); line: 2, column: 2]"

После некоторых копаний я добавил ctor по умолчанию в Foo, и это устранило ошибку. Я также должен был удалить final из участников.

Я не понимаю, почему это решение работает. Может кто-нибудь объяснить это?

Ответы [ 2 ]

0 голосов
/ 05 ноября 2018

Просто аннотируйте конструктор с помощью @JsonCreator:

@JsonCreator
public Foo(LocalDateTime start, LocalDateTime end) {
    this.start = start;
    this.end = end;
}

Как только ваши даты используют очень специфический формат , вам нужно написать собственный десериализатор для их обработки.


Если вы придерживаетесь ISO 8601 , вы можете положиться на JavaTimeModule: он предоставит вам набор сериализаторов и десериализаторы для JSR-310 типы данных .

Подробнее здесь .

0 голосов
/ 03 ноября 2018

Джексон использует отражение для создания экземпляра класса Foo, и этот механизм требует наличия конструктора по умолчанию.

После создания экземпляра класса Foo, снова используя отражение, Джексон установит значения полей этого экземпляра - в настоящее время все значения полей равны нулю. Поскольку вы объявили эти поля final, они не могут быть изменены.

Вот пример:

import java.lang.reflect.Field;

public class Reflection {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Child.class;
            Object child = clazz.newInstance();
            Field f1 = child.getClass().getDeclaredField("age");

            // Because 'age' field is private
            // we make it accesible
            f1.setAccessible(true);

            // Set the desired value
            f1.set(child, 10);
            System.out.println("Child: " + child);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Child {
    private int age;

    @Override
    public String toString() {
        return "Child [age=" + age + "]";
    }

}

Механизм сериализации / десериализации Джексона очень гибок. Существует множество аннотаций, которые вы можете установить, и конфигурации, которые вы можете сделать.

Вот еще один пример:

public class Jackson {
    public static void main(String[] args) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            String jsonAsString = "{\"age\":10}";
            Child child = mapper.readValue(jsonAsString, Child.class);
            System.out.println(child);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Приведенный выше пример не работает, поскольку в поле age нет методов доступа. Чтобы преодолеть это, мы можем добавить:

mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

и приведенный выше пример будет работать сейчас.

Очень хорошую документацию о Джексоне можно найти здесь , как указал @JB Низет в комментарии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...