Невозможно десериализовать простейший объект с окончательным полем, но Джексон - PullRequest
0 голосов
/ 24 марта 2019

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

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)

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

Ответы [ 2 ]

1 голос
/ 24 марта 2019

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

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

  • Аннотируйте конструктор, который вы хотите, чтобы Джексон вызывал, с помощью @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
}

Аннотация Lombok @Value создает только один конструктор all-args, который мы должны аннотировать с помощью @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>
0 голосов
/ 24 марта 2019

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

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