Почему JsonDeserialize не может создавать классы с помощью Lombok SuperBuilder - PullRequest
1 голос
/ 07 мая 2020

Я пытаюсь создать объектную модель, которая представляет иерархию вложенных расположений устройств. Например, «колода» содержит «лоток для слайдов», который содержит один или несколько «слайдов». Я хочу иметь возможность читать файл 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,

Суперкласс верхнего уровня:

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;

}

Подкласс Slide:

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;

}

NullLo c:

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-файл демонстрационного проекта ... но при необходимости могу найти способ поделиться им. Спасибо!

1 Ответ

1 голос
/ 07 мая 2020

Я думаю, что проблема root связана со значениями @JsonDeserialize аннотации builder, определенными для трех основных подклассов, потому что они кажутся ссылками abstract class. Это также объясняет сообщение об ошибке, которое вы получаете.

Из Lombok @SuperBuilder документации ref :

Чтобы гарантировать безопасность типов, @SuperBuilder генерирует два внутренних класса построителя для каждого аннотированного класса, один абстрактный и один конкретный класс с именами FoobarBuilder и FoobarBuilderImpl (где Foobar - это имя аннотированного класса).

I считаю, что обновление следующих значений @JsonDeserialize аннотации builder поможет решить проблему:

В подклассе Deck:

@JsonDeserialize(builder = Deck.DeckBuilderImpl.class)

В подклассе SlideTray :

@JsonDeserialize(builder = SlideTray.SlideTrayBuilderImpl.class)

В подклассе Slide:

@JsonDeserialize(builder = Slide.SlideBuilderImpl.class)

Дополнительное примечание относительно BuilderImpl обновлений вручную:

@SuperBuilder документация ref включает следующую вспомогательную информацию относительно этого topi c:

Настройка кода, сгенерированного @SuperBuilder, ограничивается добавлением новых методы или аннотации к классам построителя и предоставление cus Том реализации методов set, builder () и build (). Вы должны убедиться, что заголовки объявления класса построителя совпадают с заголовками, которые были бы созданы lombok. Из-за частого использования дженериков мы настоятельно рекомендуем скопировать заголовок определения класса компоновщика из ненастроенного кода с удаленным блоком.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...