多构造器Java实体类添加新字段后使用Jackson ObjectMapper反序列化失败问题
首先直接回答你的核心疑问:Jackson在反序列化时的构造器选择逻辑是这样的:
- 如果类存在无参默认构造器,Jackson会优先使用它创建对象,然后通过setter方法为各个字段赋值(前提是字段有对应的setter,或者字段本身是公开的)。
- 如果没有无参构造器,但有带参数的构造器,Jackson会尝试通过参数名与JSON字段名的匹配(或
@JsonProperty注解映射),直接用该构造器创建对象,这时候不会调用setter。 - 当存在多个带参数的构造器时,Jackson需要明确知道该使用哪一个,否则就会抛出你遇到的「找不到合适Creator」错误。
你的问题根源分析
你已经通过@NoArgsConstructor生成了无参构造器,按道理应该能正常工作,但问题出在你新增了自定义的带参构造器(ABC(String a,Long b,String c)),同时Lombok的@AllArgsConstructor还会自动生成一个包含所有字段(id、number、a、b、c)的全参构造器。这时候Jackson在处理带c字段的JSON时,会发现有多个带参数的构造器,无法确定该选用哪一个,进而抛出报错。
具体解决方案
方法1:让Jackson忽略自定义构造器(最简便)
给你所有自定义的非全参构造器添加@JsonIgnore注解,告诉Jackson不要用这些构造器做反序列化,转而使用无参构造器+setter的方式:
@Getter @Setter @ToString @Document(collection = "abc") @NoArgsConstructor @AllArgsConstructor public class ABC implements Serializable { @Id private String id; @Indexed private String number; private String a; private Long b; private String c; @JsonIgnore public ABC(Long b) { this.b=b; } @JsonIgnore public ABC(String a,Long b) { this.b=b; this.a=a; } @JsonIgnore public ABC(String a,Long b,String c) { this.b=b; this.a=a; this.c=c; } }
这种方式既保留了你原有代码的兼容性,又能让Jackson正常处理带c字段的新JSON和不带c的老JSON。
方法2:指定用于反序列化的构造器
如果你希望Jackson使用某个特定的带参构造器来反序列化,可以给该构造器添加@JsonCreator注解,并给每个参数用@JsonProperty指定对应的JSON字段名:
@JsonCreator public ABC(@JsonProperty("id") String id, @JsonProperty("number") String number, @JsonProperty("a") String a, @JsonProperty("b") Long b, @JsonProperty("c") String c) { this.id = id; this.number = number; this.a = a; this.b = b; this.c = c; }
不过这种方式需要把所有可能的字段都加到构造器参数里,不如第一种方法简洁,适合你明确想通过构造器初始化对象的场景。
方法3:清理Lombok注解冲突
如果你不需要Lombok自动生成的全参构造器,可以去掉@AllArgsConstructor注解,只保留你自己定义的构造器(同时必须保留@NoArgsConstructor)。不过这种方式需要你手动维护构造器,后续新增字段时还要同步修改,不如用Lombok省心。
内容的提问来源于stack exchange,提问作者darth_vader

