【问题标题】:Lombok 1.18.0 and Jackson 2.9.6 not working togetherLombok 1.18.0 和 Jackson 2.9.6 不能一起工作
【发布时间】:2023-04-10 12:40:01
【问题描述】:

更新后反序列化失败。

我将我的微服务从Spring 1.5.10.RELEASE 更新为Spring 2.0.3.RELEASE,并将lombok1.16.14 更新为1.18.0jackson-datatype-jsr3102.9.4 更新为2.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]

【问题讨论】:

  • 您是否尝试将构造函数添加到 ErrorDetail 类?

标签: java spring-boot jackson lombok jackson-databind


【解决方案1】:

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

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

更新:只有@NoArgsConstructor@Getter(没有@Setter)也可以工作(因为INFER_PROPERTY_MUTATORS=true)。通过这种方式,您可以保持类不可变,至少在常规(非反射)代码中是这样。

解决方案 2: 配置 lombok 以再次生成注释,using a lombok.config file 包含行 lombok.anyConstructor.addConstructorProperties = true。 (如果您正在使用模块,请确保 java.desktop 在您的模块路径中。)添加 lombok.config 文件后清理并重新编译。

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

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

【讨论】:

  • 谢谢。第一个选项成功了。但是第二个没有用。
  • Ad 2) 您是否将lombok.config 添加到项目的根文件夹中?另请注意,您必须在执行此操作后清理并重新编译。
  • @JanRieke - 你能帮我解决这个相关问题吗 - stackoverflow.com/questions/56245674/…
  • NoArgsConstructor 不仅会失去不变性而且不能检查null
  • Solution 1 在我的情况下还不够,我还必须添加 @AllArgsConstructor 以消除另一个错误 constructor ... in class .. . 不能应用于给定类型。使用 Java 1.8.0_265、Lombok 1.18.4、Jackson 2.9.5
【解决方案2】:

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


让 jackson 和 lombok 很好地协同工作的最佳方法是始终让您的 DTO 不可变,并告诉 jackson 使用构建器反序列化到您的对象中。

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

为此,您需要两个注解: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 {
   }
}

【讨论】:

  • Pffew 有很多样板注释,用于打算删除样板的库。
  • @ChristopheBouhier 大多数类级别的注释可以在创建 Jackson ObjectMapper 时配置一次,我假设它们包含在此处用于演示目的 ;-)
  • 实际上所有这些东西都被合并到一个注释中:@Jacksonizedprojectlombok.org/features/experimental/Jacksonized
【解决方案3】:

您想反序列化具有 final 字段的类。所以你需要声明一个包含要反序列化的最终字段的构造函数。

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

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

@JsonCreator
public ErrorDetail(@JsonProperty("heading") String heading, @JsonProperty("detail") String detail) {
    this.heading = heading;
    this.detail = detail;
}
}

并且当使用映射器进行反序列化时需要MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS设置此属性false

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

【讨论】:

    【解决方案4】:

    解决方案 4

    • 自己编写 NoArgsConstructor。这至少对我有用 lombok 1.18.8 和 Jackson 2.9.9
        @Builder
        @Getter
        @AllArgsConstructor
        public class EventDTO {
    
            private String id;
            private Integer isCancelled;
    
            private String recurringEventId;
    
            private String summary;
            private String description;
            private String location;
            private String startDateTime;
            private String endDateTime;
    
            /**
             * Make Jackson happy
             */
            public EventDTO() {
            }
        }
    

    【讨论】:

      【解决方案5】:

      在我看来,使用@Data 注释是不好的方法。 请将@Data改为@Getting@Setter@EqualsAndHashcode等..

      如果有帮助,请写在这里。

      更新

      我建议@Data创建@RequiredArgsConstructor,它是带有final字段的构造函数,没有private String type

      【讨论】:

        猜你喜欢
        • 2018-05-20
        • 2021-11-23
        • 1970-01-01
        • 1970-01-01
        • 2014-12-05
        • 1970-01-01
        • 2018-07-16
        • 2014-01-07
        • 2019-08-17
        相关资源
        最近更新 更多