Я просто хочу сериализовать и десериализовать простой неизменяемый объект и не понимаю, почему я не могу сделать это с помощью Джексона.
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)
Я понимаю, что ответ должен быть простым, но это самый распространенный случай, который должен работать из коробки, не так ли?




В 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>