如何在不使用 ObjectMapper 的情况下反序列化不可变物件缺省构造函数?

我想用 com.fasterxml.jackson.datind. ObjectMapper 序列化和反序列化一个不可变物件。

不可变类如下所示(只有3个内部属性、 getter 和构造函数) :

public final class ImportResultItemImpl implements ImportResultItem {


private final ImportResultItemType resultType;


private final String message;


private final String name;


public ImportResultItemImpl(String name, ImportResultItemType resultType, String message) {
super();
this.resultType = resultType;
this.message = message;
this.name = name;
}


public ImportResultItemImpl(String name, ImportResultItemType resultType) {
super();
this.resultType = resultType;
this.name = name;
this.message = null;
}


@Override
public ImportResultItemType getResultType() {
return this.resultType;
}


@Override
public String getMessage() {
return this.message;
}


@Override
public String getName() {
return this.name;
}
}

但是,当我运行这个单元测试时:

@Test
public void testObjectMapper() throws Exception {
ImportResultItemImpl originalItem = new ImportResultItemImpl("Name1", ImportResultItemType.SUCCESS);
String serialized = new ObjectMapper().writeValueAsString((ImportResultItemImpl) originalItem);
System.out.println("serialized: " + serialized);


//this line will throw exception
ImportResultItemImpl deserialized = new ObjectMapper().readValue(serialized, ImportResultItemImpl.class);
}

我有个例外:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class eu.ibacz.pdkd.core.service.importcommon.ImportResultItemImpl]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: {"resultType":"SUCCESS","message":null,"name":"Name1"}; line: 1, column: 2]
at
... nothing interesting here

这个例外要求我创建一个缺省构造函数,但这是一个不可变物件,所以我不想要它。它将如何设置内部属性?这完全会使 API 的用户感到困惑。

所以我的问题是: 我能不能在没有缺省构造函数的情况下反序列化不可变对象?

72666 次浏览

要让 Jackson 知道如何为反序列化创建对象,可以为构造函数使用 @JsonCreator@JsonProperty注释,如下所示:

@JsonCreator
public ImportResultItemImpl(@JsonProperty("name") String name,
@JsonProperty("resultType") ImportResultItemType resultType,
@JsonProperty("message") String message) {
super();
this.resultType = resultType;
this.message = message;
this.name = name;
}

你可以使用私人缺省构造函数,然后杰克逊将填补领域通过反思,即使他们是私人决赛。

编辑: 如果你有继承权,对父类使用受保护/包保护的缺省构造函数。

谢尔盖 · 佩图宁的第一个答案是正确的。 但是,我们可以通过删除构造函数的每个参数上的冗余@JsonProperty 注释来简化代码。

这可以通过在 ObjectMapper 中添加 com.fasterxml.jackson.module.parnames.Parameter terNamesModule 来完成:

new ObjectMapper()
.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))

(顺便说一句: 这个模块是在 SpringBoot 中默认注册的。如果使用来自 JacksonObjectMapperConfiguration 的 ObjectMapper bean,或者使用 bean Jackson2ObjectMapperBuilder 创建自己的 ObjectMapper,则可以跳过手动注册模块)

例如:

public class FieldValidationError {
private final String field;
private final String error;


@JsonCreator
public FieldValidationError(String field,
String error) {
this.field = field;
this.error = error;
}


public String getField() {
return field;
}


public String getError() {
return error;
}
}

ObjectMapper 对这个 json 进行反序列化,没有任何错误:

{
"field": "email",
"error": "some text"
}

现在是2021年,我也遇到了同样的问题。不幸的是,这个帖子里之前的答案对我的情况没有帮助,因为:

  1. 我需要反序列化 java.net.HttpCookie类对象。我不能修改它。
  2. 由于某些原因,我使用了 jackson-database-2.11.0版本。

这就是我找到的解决办法。 你可以使用反序列化问题处理程序:

    objectMapper.addHandler(new DeserializationProblemHandler() {




@Override
public Object handleMissingInstantiator(DeserializationContext ctxt, Class<?> instClass, ValueInstantiator valueInsta, JsonParser p, String msg) throws IOException {
return super.handleMissingInstantiator(ctxt, instClass, valueInsta, p, msg);
}


});

只要返回你所期望的对象

从 Java14和 Jackson 2.12.0开始,您可以像下面这样使用 记录类:

public record ImportResultItemImpl(String name, ImportResultItemType resultType, String message) implements ImportResultItem {
    

public ImportResultItemImpl(String name, ImportResultItemType resultType) {
// calling the default constructor
this(name, resultType, null);
}
}

此外,您还必须重构您的接口和用法,因为 record 的 getter 不以 getis开头:

public interface ImportResultItem {


String name();


ImportResultItemType resultType();


String message();
}