我想使用com.fasterxml.jackson.databind.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的用户。
所以我的问题是:我可以以某种方式对没有默认构造函数的不可变对象进行反序列化吗?
答案 0 :(得分:107)
要让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;
}
答案 1 :(得分:26)
您可以使用私人默认构造函数,然后杰克逊将通过反射填充字段,即使它们是私人决赛。
编辑:如果你有继承,则使用受保护/受包保护的父类的默认构造函数。
答案 2 :(得分:4)
谢尔盖·佩图宁(Sergei Petunin)的第一个答案是正确的。 但是,我们可以通过删除构造函数每个参数上的多余@JsonProperty注释来简化代码。
可以通过将com.fasterxml.jackson.module.paramnames.ParameterNamesModule添加到ObjectMapper中来完成:
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"
}