java - 为什么 JsonDeserialize 无法使用 Lombok SuperBuilder 构造类

标签 java lombok jackson-databind

我正在尝试创建一个表示嵌套设备位置层次结构的对象模型。例如,“甲板”包含一个“幻灯片托盘”,其中包含一张或多张“幻灯片”。我希望能够读取包含系统层次结构/配置的 json 文件。我想在我的类中使用 Lombok 构建器,这样我就可以在需要时在代码中安全地生成 json 文件。更常见的用例是读取 json 文件以在应用程序启动时创建 pojo。使用构建器生成 json 文件效果很好。但是,我还没有将文件反序列化回 pojo 的。

这是我收到的错误:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `my.org.Deck$DeckBuilder` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (String)"{"type":"Deck","locNumber":1,

顶级父类(super class)是这样的:

package my.org;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Getter;
import lombok.Singular;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;

import java.awt.geom.Point2D;
import java.util.List;


@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Deck.class, name = "Deck"),
        @JsonSubTypes.Type(value = SlideTray.class, name = "SlideTray"),
        @JsonSubTypes.Type(value = Slide.class, name = "Slide"),
        @JsonSubTypes.Type(value = NullLoc.class, name = "null"),
})
@SuperBuilder
@Getter
@Accessors(fluent = true, chain = true)
@JsonDeserialize(builder = BaseLocationType.BaseLocationTypeBuilder.class)
public class BaseLocationType<T extends BaseLocationType> {

    @JsonProperty("locNumber")
    private int locNumber;

    @JsonProperty("posRelativeToParent")
    private Point2D.Double positionRelativeToParent;

    @Singular
    @JsonProperty("childLocs")
    private List<T> childLocs;

}

甲板子类:

package my.org;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;

@SuperBuilder
@Getter
@EqualsAndHashCode(callSuper = true)
@Accessors(fluent = true, chain = true)
@JsonDeserialize(builder = Deck.DeckBuilder.class)
public class Deck extends BaseLocationType<SlideTray> {

    private String deckField1;

    private String deckField2;


}

SlideTray 子类:

package my.org;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;

@SuperBuilder
@Getter
@EqualsAndHashCode(callSuper = true)
@Accessors(fluent = true, chain = true)
@JsonDeserialize(builder = SlideTray.SlideTrayBuilder.class)
public class SlideTray extends BaseLocationType<Slide> {

    private String slideTrayField1;

}

幻灯片子类:

package my.org;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;

@SuperBuilder
@Getter
@EqualsAndHashCode(callSuper = true)
@Accessors(fluent = true, chain = true)
@JsonDeserialize(builder = Slide.SlideBuilder.class)
public class Slide extends BaseLocationType<NullLoc> {

    private String slideField1;

}

空定位:

package my.org;

import lombok.experimental.SuperBuilder;

@SuperBuilder
public class NullLoc extends BaseLocationType<NullLoc> {

    // no fields or builder, etc
}

测试代码 - 失败并在 mapper.readValue() 上出现上述异常:

// create 1 deck with 1 slideTray that has 2 slides
Deck.DeckBuilder<?, ?> deckBuilder = Deck.builder()
        .locNumber(1)
        .positionRelativeToParent(new Point2D.Double(1.0, 1.0))
        .deckField1("deck f1 data")
        .deckField2("deck f2 data")
        .childLoc(SlideTray.builder()
                .locNumber(2)
                .positionRelativeToParent(new Point2D.Double(2.0, 2.0))
                .slideTrayField1("slide tray f1 data")
                .childLoc(Slide.builder()
                        .locNumber(3)
                        .positionRelativeToParent(new Point2D.Double(3.0, 3.0))
                        .slideField1("child1-slide f1 data")
                        .build())
                .childLoc(Slide.builder()
                        .locNumber(4)
                        .positionRelativeToParent(new Point2D.Double(4.0, 4.0))
                        .slideField1("child2-slide f1 data")
                        .build()).build());

Deck deckPojo = deckBuilder.build();

// serialize the pojo's
String json = new ObjectMapper().writeValueAsString(deckPojo);

// de-serialize the json back into the pojo's
ObjectMapper mapper = new ObjectMapper();
Deck deckPojoDeserialized = mapper.readValue(json, Deck.class);

生成的json:

{
  "type": "Deck",
  "locNumber": 1,
  "posRelativeToParent": {
    "x": 1.0,
    "y": 1.0
  },
  "childLocs": [
    {
      "type": "SlideTray",
      "locNumber": 2,
      "posRelativeToParent": {
        "x": 2.0,
        "y": 2.0
      },
      "childLocs": [
        {
          "type": "Slide",
          "locNumber": 3,
          "posRelativeToParent": {
            "x": 3.0,
            "y": 3.0
          },
          "childLocs": []
        },
        {
          "type": "Slide",
          "locNumber": 4,
          "posRelativeToParent": {
            "x": 4.0,
            "y": 4.0
          },
          "childLocs": []
        }
      ]
    }
  ]
}

注意:我在 stackoverflow 中没有看到上传演示项目 zip 文件的选项...但可以找到一种方法来共享(如果需要)。 谢谢!

最佳答案

我认为根本问题与跨三个主要子类定义的 @JsonDeserialize 注释 builder 值有关,因为它们似乎是抽象 class 引用。这也可以解释您收到的错误消息。

摘自Lombok @SuperBuilder 文档 ref :

To ensure type-safety, @SuperBuilder generates two inner builder classes for each annotated class, one abstract and one concrete class named FoobarBuilder and FoobarBuilderImpl (where Foobar is the name of the annotated class).

我相信更新以下@JsonDeserialize注释builder值将有助于解决该问题:

Deck子类中:

@JsonDeserialize(builder = Deck.DeckBuilderImpl.class)

SlideTray子类中:

@JsonDeserialize(builder = SlideTray.SlideTrayBuilderImpl.class)

Slide子类中:

@JsonDeserialize(builder = Slide.SlideBuilderImpl.class)

有关 BuilderImpl 手动更新的附加说明:

@SuperBuilder 文档 ref包括以下与此主题相关的支持信息:

Customizing the code generated by @SuperBuilder is limited to adding new methods or annotations to the builder classes, and providing custom implementations of the 'set', builder(), and build() methods. You have to make sure that the builder class declaration headers match those that would have been generated by lombok. Due to the heavy generics usage, we strongly advice to copy the builder class definition header from the uncustomized delomboked code.

关于java - 为什么 JsonDeserialize 无法使用 Lombok SuperBuilder 构造类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61647171/

相关文章:

java - 在 jackson 的属性内添加属性

java - Jackson 数据绑定(bind)在 IDE 环境中工作但不在 jar 中

java - jackson 序列化不包括 double 值 0.0

java - 如何使用spring websocket向自定义用户发送自定义消息?

java - 是否应该覆盖哈希码以将对象存储在 ArrayList 中?

java - com.google.android :android:jar:4. 4.2_r3 在哪里?

java - Lombok 不适用于 spring-boot-maven-plugin

Maven-tycho 表示尽管其他依赖项有效,但缺少 Lombok 所需的 Artifact

java - 在 Eclipse 中将 codenameone 与 lombok 结合使用

java - 如何在 Java 8 中使用 Spring Aspect 保留方法参数名称