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

gee*_*jay 73 java json jackson

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

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

冲突的基于属性的创建者:已经有... {interface org.codehaus.jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator()}],遇到......,注释:{interface org.codehaus. jackson.annotate.JsonCreator @ org.codehaus.jackson.annotate.JsonCreator()}]

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

谢谢

Per*_*ion 109

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


Bri*_*ice 55

对于Jackson databind 2.7.0,这仍然适用.

杰克逊@JsonCreator注释2.5的javadoc杰克逊的注释文档语法(构造函数小号和工厂方法小号)让认为确实是一个可以标记多个构造.

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

查看创建者被识别的代码,看起来杰克逊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);
}
Run Code Online (Sandbox Code Playgroud)
  • 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");
Run Code Online (Sandbox Code Playgroud)

但是这段代码可以工作:

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

这有点hacky,可能不会成为未来的证据.


关于对象创建如何工作的文档很模糊; 从我从代码中收集的内容来看,它可以混合使用不同的方法:

例如,可以使用带注释的静态工厂方法 @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");
Run Code Online (Sandbox Code Playgroud)

它的工作原理但并不理想.最后它可能有意义,例如,如果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");
Run Code Online (Sandbox Code Playgroud)

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

  • 恕我直言这应该是公认的答案,因为它提供了完整的解释和很好的例子 (6认同)

Tia*_*ins 6

如果我做对了您要实现的目标,则可以解决此问题而无需构造函数重载

如果仅要将空值放在JSON或Map中不存在的属性中,则可以执行以下操作:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Person {
    private String name;
    private Integer age;
    public static final Integer DEFAULT_AGE = 30;

    @JsonCreator
    public Person(
        @JsonProperty("name") String name,
        @JsonProperty("age") Integer age) 
        throws IllegalArgumentException {
        if(name == null)
            throw new IllegalArgumentException("Parameter name was not informed.");
        this.age = age == null ? DEFAULT_AGE : age;
        this.name = name;
    }
}
Run Code Online (Sandbox Code Playgroud)

当我找到您的问题时就是我的情况。我花了一些时间弄清楚如何解决它,也许那正是您想要做的。 @Brice解决方案对我不起作用。

  • 最佳答案恕我直言 (2认同)