由于 lombok 创建的非默认构造函数,Jackson 反序列化失败
Jackson Deserialization Fails because of non-default constructor created by lombok
Jackson 可以在 2.6.5 中为以下 class 反序列化 json,但在 2.8.8 中失败。
型号:
public static class Parent {
public long id;
public List<Child> children;
}
@RequiredArgsConstructor
public static class Child {
public long childId;
@NonNull
@JsonIgnore
public Parent parent;
public Child() { }
}
JSON:
{
"id": 1,
"children": [
{
"childId": 2
}
]
}
例外情况是:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "childId" (class Parent), not marked as ignorable (2 known properties: "children", "id"])
我发现由 lombok 创建的 Child 构造函数导致了这个错误。当我摆脱 lombok 注释或者如果我手动创建构造函数时,这种情况就会停止。无论哪种方式,它都应该使用无参数的 Child() 构造函数。是什么导致了这个问题?
Lombok 将注解@ConstructorProperties({"parent"})
添加到生成的构造函数中。在 Jackson 2.8.8 中,这导致构造函数被视为 "delegate creator".
委托创建者允许 Jackson 将一种类型的对象 json 反序列化为另一种类型的 Java 对象。
在这种情况下,因为 lombok 生成构造函数 @ConstructorProperties({"parent"}) Child(Parent parent) {...}
Jackson 将尝试将子 json 反序列化为父对象,然后可以将其传递到构造函数中以创建子对象。然后它抛出异常,因为 childId 不是 Parent 中的字段。
一个解决方法是使用自定义 JacksonAnnotationIntrospector
配置用于反序列化 JSON 的 ObjectMapper
,这样它就不会将构造函数解释为委托创建者。
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector().setConstructorPropertiesImpliesCreator(false));
更新
lombok 项目的 1.16.20 版本确实默认 lombok.anyConstructor.suppressConstructorProperties 为 true,正如 Roel 在他的评论中指出的那样。
这使得将 lombok 升级到最新版本是解决此问题的另一个方法。
Jackson 可以在 2.6.5 中为以下 class 反序列化 json,但在 2.8.8 中失败。
型号:
public static class Parent {
public long id;
public List<Child> children;
}
@RequiredArgsConstructor
public static class Child {
public long childId;
@NonNull
@JsonIgnore
public Parent parent;
public Child() { }
}
JSON:
{
"id": 1,
"children": [
{
"childId": 2
}
]
}
例外情况是:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "childId" (class Parent), not marked as ignorable (2 known properties: "children", "id"])
我发现由 lombok 创建的 Child 构造函数导致了这个错误。当我摆脱 lombok 注释或者如果我手动创建构造函数时,这种情况就会停止。无论哪种方式,它都应该使用无参数的 Child() 构造函数。是什么导致了这个问题?
Lombok 将注解@ConstructorProperties({"parent"})
添加到生成的构造函数中。在 Jackson 2.8.8 中,这导致构造函数被视为 "delegate creator".
委托创建者允许 Jackson 将一种类型的对象 json 反序列化为另一种类型的 Java 对象。
在这种情况下,因为 lombok 生成构造函数 @ConstructorProperties({"parent"}) Child(Parent parent) {...}
Jackson 将尝试将子 json 反序列化为父对象,然后可以将其传递到构造函数中以创建子对象。然后它抛出异常,因为 childId 不是 Parent 中的字段。
一个解决方法是使用自定义 JacksonAnnotationIntrospector
配置用于反序列化 JSON 的 ObjectMapper
,这样它就不会将构造函数解释为委托创建者。
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector().setConstructorPropertiesImpliesCreator(false));
更新
lombok 项目的 1.16.20 版本确实默认 lombok.anyConstructor.suppressConstructorProperties 为 true,正如 Roel 在他的评论中指出的那样。 这使得将 lombok 升级到最新版本是解决此问题的另一个方法。