如何使用 JsonCreator 反序列化具有重载构造函数的类

2022-08-31 10:54:39

我正在尝试使用Jackson 1.9.10反序列化此类的实例:

public class Person {

@JsonCreator
public Person(@JsonProperty("name") String name,
        @JsonProperty("age") int age) {
    // ... person with both name and age
}

@JsonCreator
public Person(@JsonProperty("name") String name) {
    // ... person with just a name
}
}

当我尝试这个时,我得到以下

相互冲突的基于财产的创作者:已经有...{interface org.codehaus.jackson.annotate.JsonCreator @org.codehaus.jackson.annotate.JsonCreator()}], encountered ... , annotations: {interface org.codehaus.jackson.annotate.JsonCreator @org.codehaus.jackson.annotate.JsonCreator()}]

有没有办法使用 Jackson 反序列化具有重载构造函数的类?

谢谢


答案 1

虽然没有正确记录,但每种类型的创建者只能有一个创建者。您可以在类型中拥有任意数量的构造函数,但其中只有一个构造函数应该有批注。@JsonCreator


答案 2

编辑:看哪,在Jackson维护者的博客文章中,似乎2.12可能会在构造函数注入方面看到改进。(本次编辑时的当前版本是 2.11.1)

改进构造函数创建者的自动检测,包括使用不明确的 1 参数构造函数(委派与属性)解决/缓解问题


对于 Jackson databind 2.7.0 来说,这仍然适用。

Jackson @JsonCreator注释 2.5 javadocJackson 注释文档语法(构造函数s 和工厂方法s)确实让人相信可以标记多个构造函数。

标记批注,可用于将构造函数和工厂方法定义为用于实例化关联类的新实例的标记批注。

查看标识创建者的代码,看起来 Jackson 忽略了重载构造函数,因为它只检查构造函数的第一个参数CreatorCollector

Class<?> oldType = oldOne.getRawParameterType(0);
Class<?> newType = newOne.getRawParameterType(0);

if (oldType == newType) {
    throw new IllegalArgumentException("Conflicting "+TYPE_DESCS[typeIndex]
           +" creators: already had explicitly marked "+oldOne+", encountered "+newOne);
}
  • oldOne是第一个确定的构造函数创建者。
  • newOne是重载构造函数创建者。

这意味着这样的代码将不起作用。

@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
    this.country = "";
}

@JsonCreator
public Phone(@JsonProperty("country") String country, @JsonProperty("value") String value) {
    this.value = value;
    this.country = country;
}

assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336"); // raise error here
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");

但是这段代码会起作用:

@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
    enabled = true;
}

@JsonCreator
public Phone(@JsonProperty("enabled") Boolean enabled, @JsonProperty("value") String value) {
    this.value = value;
    this.enabled = enabled;
}

assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");

这有点笨拙,可能不是未来的证明


文档对对象创建的工作原理含糊不清;从我从代码中收集到的信息来看,可以混合不同的方法:

例如,可以将静态工厂方法注释为@JsonCreator

@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
    enabled = true;
}

@JsonCreator
public Phone(@JsonProperty("enabled") Boolean enabled, @JsonProperty("value") String value) {
    this.value = value;
    this.enabled = enabled;
}

@JsonCreator
public static Phone toPhone(String value) {
    return new Phone(value);
}

assertThat(new ObjectMapper().readValue("\"+336\"", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");

它有效,但不理想。最后,它可能是有意义的,例如,如果JSON是动态的,那么也许应该使用委托构造函数来处理有效负载变化,而不是使用多个带注释的构造函数。

另请注意,Jackson 按优先级对创建者进行排序,例如在以下代码中:

// Simple
@JsonCreator
public Phone(@JsonProperty("value") String value) {
    this.value = value;
}

// more
@JsonCreator
public Phone(Map<String, Object> properties) {
    value = (String) properties.get("value");
    
    // more logic
}

assertThat(new ObjectMapper().readValue("\"+336\"", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\"}", Phone.class).value).isEqualTo("+336");
assertThat(new ObjectMapper().readValue("{\"value\":\"+336\",\"enabled\":true}", Phone.class).value).isEqualTo("+336");

这一次,Jackson 不会引发错误,但 Jackson 只会使用委托构造函数 ,这意味着永远不会使用。Phone(Map<String, Object> properties)Phone(@JsonProperty("value") String value)