如何使用 Jackson 反序列化外部 Lombok 构建器 class

How to use Jackson to deserialize external Lombok builder class

我有一个第 3 方 Lombok 构建器 POJO,我无法修改它,我想使用 jackson 对其进行序列化。值得注意的是,它 not 有一个 NoArgsConstructor。

@Data
@Builder
public class ExternalClass {
   private String name;
   private String data; 
   // etc.
} 

从表面上看,这似乎很简单,但在实践中却令人难以置信地沮丧,因为每个可能的选择似乎都被不同的并发症所抵消。本质上,我无法让 external Lombok builder 与 jackson mixin 一起工作。

Lombok 生成 .name(String name) 风格的流畅设置器,而 Jackson 的内置构建器反序列化器期望 .withName(String name)。 Lombok 文档和其他地方的食谱(例如 here)建议在预先声明的内部存根构建器上结合使用 @JsonDeserialize(builder=ExternalClass.ExternalClassBuilder.class)@JsonPOJOBuilder(withPrefix="")。但这是不可能的,因为 Lombok class 在外部库中。

将这些注释应用于混入没有任何效果。

@JsonDeserialize(ExternalClass.ExternalClassBuilder.class)
public abstract class ExternalClassMixin {
   @JsonPOJOBuilder(withPrefix="")
   public static ExternalClassBuilder {
   }
} 

我发现唯一可行的方法是利用 @Builder 创建的包访问 AllArgsConstructor 并使用以下构造函数填充混入

public abstract class ExternalClassMixin {
   @JsonCreator public ExternalClassMixin(
      @JsonProperty("name") String name,
      @JsonProperty("data") String data,
      // etc.
  ) {} 
} 

这显然是不可取的,因为它需要明确地对每个 class 属性 进行迭代和硬编码,使得 mixin 容易受到外部 POJO 中的任何更改的影响。

我的问题是 - 是否有一种健壮的、可维护的方法来使用 Jackson 序列化这个外部构建器 class 而无需修改它,使用 mixin 或者可能是一个完整的反序列化器?

更新

我实现了@jan-rieke 的出色答案,包括使用反射寻找内部构建器的建议 class。

...
public Class<?> findPOJOBuilder(AnnotatedClass ac) {
   Class<?> innerBuilder;
   try {
      innerBuilder = Class.forName(ac.getName()+"$"+ac.getRawType().getSimpleName()+"Builder");
      log.info("Builder found: {}", ac.getName());
      return innerBuilder;
   } catch( ClassNotFoundException e ) {
      return super.findPOJOBuilder(ac);
   }
}

您可以按如下方式自定义 ObjectMapper

    ObjectMapper mapper = new ObjectMapper();
    mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
        @Override
        public Class<?> findPOJOBuilder(AnnotatedClass ac) {
            if (ExternalClass.class.equals(ac.getRawType())) {
                return ExternalClass.ExternalClassBuilder.class;
            }
            return super.findPOJOBuilder(ac);
        }

        @Override
        public Value findPOJOBuilderConfig(AnnotatedClass ac) {
            if (ac.hasAnnotation(JsonPOJOBuilder.class)) {
                return super.findPOJOBuilderConfig(ac);
            }
            return new JsonPOJOBuilder.Value("build", "");
        }
    });

这将

  • 明确配置 ExternalClass 的反序列化使用其构建器,并且
  • 将构建器 setter 方法的默认前缀设置为 ""(存在 @JsonPOJOBuilder 注释时除外)。

如果您不想在 findPOJOBuilder() 中明确列出所有外部 classes,您当然可以通过编程方式查看 class 以检查它是否具有内部 class 看起来像个建筑师。

这可以通过创建两个 mixin 来实现:一个用于 ExternalClass(指定要使用的构建器),一个用于 ExternalClass.ExternalClassBuilder(指定构建器方法中缺少前缀)。

@JsonDeserialize(builder = ExternalClass.ExternalClassBuilder.class)
public interface ExternalClassMixin {
}

@JsonPOJOBuilder(withPrefix="")
public interface ExternalClassBuilderMixin {
}

这会以所需方式序列化和反序列化 JSON:

String json = "{\"name\": \"The Name\", \"data\": \"The Data\"}";

ObjectMapper mapper = new ObjectMapper()
        .addMixIn(ExternalClass.class, ExternalClassMixin.class)
        .addMixIn(ExternalClass.ExternalClassBuilder.class, ExternalClassBuilderMixin.class);

System.out.println(mapper.readValue(json, ExternalClass.class));
System.out.println(mapper.writeValueAsString(mapper.readValue(json, ExternalClass.class)));

输出:

ExternalClass(name=The Name, data=The Data)
{"name":"The Name","data":"The Data"}