使@JsonAnySetter 与@Value Lombok 一起工作

Make @JsonAnySetter work with @Value Lombok

我有一个 json 对象,它有很多属性(~80 个属性)我想在 POJO 中反序列化而不手动创建所有属性。我能够通过使用 @JsonAnySetterMap 属性 来做到这一点,就像 here.

中描述的那样

现在我想通过使用 Lombok 使我的 POJO 不可变来完成这项工作。 我试过了,但它只会反序列化 idcode 属性。知道如何让它发挥作用吗?

@Value
@Builder
@EqualsAndHashCode
@JsonDeserialize(builder = Product.ProductBuilder.class)
class Product {

   @JsonProperty
   private String id;
   @JsonProperty
   private String code;
   @Getter(AccessLevel.NONE)
   @Builder.Default
   @JsonProperty
   private Map<String, Optional<Object>> any = new HashMap<>();

   @JsonAnyGetter
   public  Map<String, Optional<Object>> getAny(){
       return this.any;
   }

   @JsonAnySetter
   public void setAny(String key, Optional<Object> value){
      this.any.put(key, value);
   }

}

2021-02-01 更新:Lombok v1.18.16

从 v1.18.16 开始,Lombok 自动将 @JsonAnySetter 复制到构建器中的 @Singular 方法。结合 @Jacksonized 你可以简单地使用这个代码:

@Value
@Jacksonized
@Builder
class Product {
    private String id;
    private String code;

    @JsonAnySetter
    @Singular("any")
    private Map<String, Object> any;
}

旧的 Lombok 版本

对于以前的 Lombok 版本,这需要对生成的构建器进行一些自定义 class。

自定义 lombok 构建器只需将其内部 class header 添加到您的 class 即可。 Lombok 检测到已经有一个构建器 class 并且只添加所有不存在的东西。这意味着您可以添加自己的方法,如果这些方法恰好与 lombok 生成的方法同名,lombok 将跳过此方法。

通过这种方法,我们将构建器的 setter 方法替换为“any”,并向其添加所需的 @JsonAnySetter。我使用 LinkedHashMap 作为地图以防顺序相关;如果不是,你可以使用常规 HashMap

此外,我们替换了 build() 方法以确保您提供给构造函数的映射是不可变的。我这里用的是Guava的ImmutableMap。这将使创建的实例成为不可变值。

@Value
@Builder
@JsonDeserialize(builder = Product.ProductBuilder.class)
class Product {

    @JsonProperty
    private String id;
    @JsonProperty
    private String code;

    @Getter(onMethod_ = @JsonAnyGetter)
    private Map<String, Object> any;
    
    @JsonPOJOBuilder(withPrefix = "")
    public static class ProductBuilder {
        @JsonAnySetter
        public ProductBuilder any(String anyKey, Object anyValue) {
            if (this.any == null) {
                this.any = new LinkedHashMap<String, Object>();
            }
            this.any.put(anyKey, anyValue);
            return this;
        }
        
        public Product build() {
            return new Product(id, code, any == null ? ImmutableMap.of() : ImmutableMap.copyOf(any));
        }

    }
}