如何使用ObjectMapper在没有默认构造函数的情况下对不可变对象进行de/serialize?

Mic*_*hal 87 java json

我想使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

@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);
}
Run Code Online (Sandbox Code Playgroud)

我得到这个例外:

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
Run Code Online (Sandbox Code Playgroud)

这个异常要求我创建一个默认构造函数,但这是一个不可变对象,所以我不想拥有它.它将如何设置内部属性?它会完全混淆API的用户.

所以我的问题是:我可以以某种方式取消/序列化不可变对象而不使用默认构造函数吗?

Ser*_*nin 125

要让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;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果由于无法访问POJO而无法更改默认构造函数或@JsonCreator和@JsonProperty注释怎么办?还有一种反序列化对象的方法吗? (2认同)

tkr*_*use 30

您可以使用私有默认构造函数,然后杰克逊将通过反射填充字段,即使它们是私人决赛.

编辑:如果你有继承,并为父类使用受保护/包保护的默认构造函数.


小智 15

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

可以通过将 com.fasterxml.jackson.module.paramnames.ParameterNamesModule 添加到 ObjectMapper 中来完成:

new ObjectMapper()
        .registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))
Run Code Online (Sandbox Code Playgroud)

(顺便说一句:这个模块默认在 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

和 ObjectMapper 反序列化这个 json 没有任何错误:

{
  "field": "email",
  "error": "some text"
}
Run Code Online (Sandbox Code Playgroud)


小智 5

现在是 2021 年了,我也遇到了同样的问题。不幸的是,该线程中的先前答案对我的情况没有帮助,因为:

  1. 我需要反序列化java.net.HttpCookie 类对象。我无法修改它。
  2. 由于某些原因,我使用jackson-databind-2.11.0版本。ParameterNamesModule已在3+版本中添加。

所以,这是我为我的案例找到的解决方案。您可以只使用 DeserializationProblemHandler:

    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);
        }

    });
Run Code Online (Sandbox Code Playgroud)

只需返回您期望的对象即可