Невозможно десериализовать простейший объект с конечным полем bu Jackson

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

import java.io.IOException;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Value;

public class TestApplication {

    @Value
    static class Test {
        private final String a;
    }

    public static void main(String[] args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();

        String res = objectMapper.writeValueAsString(new Test("test"));
        System.out.println(res);

        System.out.println(objectMapper.readValue(res, Test.class));
    }
}

Это терпит неудачу с исключением:

Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.devchallange.rogatakopita.RogatakopitaApplication$Test` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)"{"a":"test"}"; line: 1, column: 2]
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1032)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1297)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
    at com.devchallange.rogatakopita.RogatakopitaApplication.main(RogatakopitaApplication.java:29)

Я понимаю, что ответ должен быть простым, но это самый распространенный случай, который должен работать из коробки, не так ли?

Предоставленный код не компилируется. Таким образом, предоставленный код не может создать предоставленное исключение.
Turing85 24.03.2019 12:39
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
1
1 744
2

Ответы 2

В java окончательный атрибут должен быть установлен в конструкторе и может быть прочитан только позже. Но позже Джексон пытается изменить этот атрибут. Таким образом, вы не можете использовать jackson для заполнения последнего поля.

При разработке неизменяемых классов Джексон должен вызывать конструктор со всеми необходимыми аргументами.

Если вы не использовали ломбок,

  • Аннотируйте конструктор, который вы хотите, чтобы Джексон вызывал, с помощью @JsonCreator.
  • Аннотируйте параметры конструктора с помощью @JsonProperty. Если вы хотите пропустить это, вы можете добавить расширение использования ParameterNamesModule.

Пример:

static class Test {

    private final String a;

    public Test() {
        a = "default";
    }

    @JsonCreator // Jackson will use this constructor during deserialization
    public Test(@JsonProperty("a") String a) { // @JsonProperty can be skipped if you use ParameterNamesModule annotation
        this.a = a;
    }

    // Getter for A
}

Аннотация Ломбока @Value создает только один конструктор всех аргументов, который нам нужно аннотировать с помощью @JsonCreator. Это можно сделать, пометив класс знаком @AllArgsConstructor(onConstructor = @__(@JsonCreator)).

Поскольку конструктор генерируется автоматически, мы не сможем аннотировать параметры с помощью @JsonProperty, поэтому вам придется использовать ParameterNamesModule.

Фрагмент кода с этими изменениями:

public class TestApplication {

    @Value
    @AllArgsConstructor(onConstructor = @__(@JsonCreator))
    static final class Test {
        private final String a;
    }

    public static void main(String[] args) throws IOException {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new ParameterNamesModule());

        String res = objectMapper.writeValueAsString(new Test("test"));
        System.out.println(res);
        System.out.println(objectMapper.readValue(res, Test.class));
    }
}

Ниже приведена зависимость maven для модуля имен параметров.

 <dependency>
      <groupId>com.fasterxml.jackson.module</groupId>
      <artifactId>jackson-module-parameter-names</artifactId>
      <version>2.9.8</version>
      <scope>compile</scope>
 </dependency>

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