龙目岛 1.18.0 和 Jackson 2.9.6 不能协同工作

更新后反序列化失败。

我将我的微服务从 更新为 ,并将“从”更新为“和”从”Spring 1.5.10.RELEASESpring 2.0.3.RELEASElombok1.16.141.18.0jackson-datatype-jsr3102.9.42.9.6

JSON 字符串 -

{"heading":"Validation failed","detail":"field must not be null"}

班级 -

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorDetail {

   private final String heading;
   private final String detail;
   private String type;
}

方法调用 -

ErrorDetail errorDetail = asObject(jsonString, ErrorDetail.class);

用于反序列化的方法 -

import com.fasterxml.jackson.databind.ObjectMapper;
// more imports and class defination.

private static <T> T asObject(final String str, Class<T> clazz) {
    try {
        return new ObjectMapper().readValue(str, clazz);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

错误 -

java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.foo.bar.ErrorDetail` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"heading":"Validation failed","detail":"field must not be null"}"; line: 1, column: 2]

答案 1

Lombok 停止在版本为 1.16.20 的构造函数上生成(参见更改日志),因为它可能会破坏使用模块的 Java 9+ 应用程序。该注释包含构造函数参数的名称(在编译类时会删除这些参数,因此这是一种解决方法,以便仍然可以在运行时检索参数名称)。由于现在默认情况下不生成批注,因此 Jackson 无法将字段名称映射到构造函数参数。@ConstructorProperties

解决方案 1:使用 and ,但您将失去不可变性(如果这对您很重要)。@NoArgsConstructor@Setter

更新:Just 和 (without ) 也可能有效(因为 INFER_PROPERTY_MUTATORS=true)。通过这种方式,您可以保持类不可变,至少从常规(非反射)代码中。@NoArgsConstructor@Getter@Setter

解决方案 2:配置龙目岛以再次生成注释,使用包含行的 lombok.config 文件。(如果您使用的是模块,请确保 位于模块路径上。添加文件后清理并重新编译。lombok.anyConstructor.addConstructorProperties = truejava.desktoplombok.config

解决方案 3:将 Jackson 的构建器支持与 lombok 的 () / 结合使用,如@Randakar对此问题的答案中所述。@Jacksonized@Builder@SuperBuilder

解决方案 4:使用(Java 8 及更高版本)进行编译时,请附加到命令。这会将构造函数和方法的参数名称存储在生成的类文件中,以便可以通过反射检索它们。javac-parameters


答案 2

编辑:这个答案现在有点过时了:有一个新的注释,来自 https://projectlombok.org/features/experimental/Jacksonized,它处理了这个答案中的大部分样板。@Jacksonized


让杰克逊和龙目岛一起玩得好的最佳方法是始终使DTO不可变,并告诉杰克逊使用构建器反序列化到您的对象中。

不可变对象是个好主意,原因很简单,当字段无法在原位修改时,编译器可以进行更积极的优化。

为此,您需要两个注释:JsonDeserialize和JsonPojoBuilder。

例:

@Builder
@Value // instead of @Data
@RequiredArgsConstructor
@NonNull // Best practice, see below.
@JsonDeserialize(builder = ErrorDetail.ErrorDetailBuilder.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class ErrorDetail {

   private final String heading;

   // Set defaults if fields can be missing, like this:
   @Builder.Default
   private final String detail = "default detail";

   // Example of how to do optional fields, you will need to configure
   // your object mapper to support that and include the JDK 8 module in your dependencies..
   @Builder.Default
   private Optional<String> type = Optional.empty()

   @JsonPOJOBuilder(withPrefix = "")
   public static final class ErrorDetailBuilder {
   }
}

推荐