Как десериализовать Java Дополнительное поле с помощью lombok builder? - PullRequest
2 голосов
/ 05 апреля 2020

Я хочу десериализовать Java Необязательное поле в Lombok Builder. Ниже приведен мой код

@JsonDeserialize(builder = AvailabilityResponse.Builder.class)
@Getter
@ToString
@EqualsAndHashCode
@Builder(setterPrefix = "with", builderClassName = "Builder", toBuilder = true)
public class AvailabilityResponse {

    private final List<Train> trainDetails;
    private final String name;
    private final Optional<String> detail;

    public static class Builder {

        @JacksonXmlProperty(localName = "TRAIN")
        private List<Train> trainDetails;

        @JacksonXmlProperty(localName = "NAME")
        private String name;

        @JacksonXmlProperty(localName = "DETAIL_TRAIN", isAttribute = true)
        private String detail;

        public AvailabilityResponse build() {
            return new AvailabilityResponse(
                trainDetails, // I have field validation here. if null or empty throwing Exception
                name, // I have field validation here. if null or empty throwing Exception
                Optional.ofNullable(detail)); // This is Optional field
        }
    }   

}    

Если я переопределю метод построителя, как показано ниже, могу десериализовать

    private Optional<String> name; // this is static builder class field

        @JacksonXmlProperty(localName = "DETAIL_TRAIN", isAttribute = true)
        public Builder detail(final String theDetail) {
            this.detail = Optional.ofNullable(theDetail);
            return this;
        }   

Я использовал setterPrefix = "with" в @Builder. Но если я переопределю вышеуказанный метод с помощью префикса «with», он не будет работать.

Пожалуйста, кто-нибудь поможет мне добиться этого

1 Ответ

2 голосов
/ 05 апреля 2020

Джексон поддерживает Optional, используя его jackson-datatype-jdk8 модуль. Вы можете просто добавить его в раздел <dependencies> в вашем pom.xml:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
    <version>2.10.3</version>
</dependency>

Затем инициализируйте свой маппер следующим образом:

ObjectMapper mapper = new XmlMapper().registerModule(new Jdk8Module());

Этот маппер автоматически обнаружит Optional s: Если поле XML имеет значение, оно будет заключено в Optional; если поле XML пустое (т. е. <DETAIL_TRAIN/>), то результатом будет Optional.empty(). (В вашем случае у вас есть атрибут; он не может быть пустым.)

Джексон отображает поля или атрибуты, которые не существуют в XML на null (или, точнее, он просто не вызывает установщик, поэтому значение поля все равно будет значением по умолчанию). Если вы также хотите, чтобы в этом случае был пустой Optional, вам нужно установить для поля по умолчанию значение Optional.empty() и добавить к этому полю @Builder.Default.

Наконец, lombok может автоматически копировать ваши аннотации от полей до созданного строителя. Вы должны указать lombok, какие аннотации копировать, используя lombok.config файл в вашем проекте root, содержащий:

lombok.copyableAnnotations += com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty
config.stopBubbling = true

Это означает, что вам вообще не нужно настраивать свой класс построителя:

@JsonDeserialize(builder = AvailabilityResponse.Builder.class)
@Getter
@ToString
@EqualsAndHashCode
@Builder(setterPrefix = "with", builderClassName = "Builder", toBuilder = true)
public class AvailabilityResponse {

    @JacksonXmlProperty(localName = "TRAIN")
    @JsonProperty("TRAIN")
    private final List<Train> trainDetails;

    @JacksonXmlProperty(localName = "NAME")
    @JsonProperty("NAME")
    private final String name;

    @JacksonXmlProperty(localName = "DETAIL_TRAIN", isAttribute = true)
    @JsonProperty("DETAIL_TRAIN")
    @Builder.Default
    private final Optional<String> detail = Optional.empty();
}

Обратите внимание, что вам также нужно @JsonProperty аннотаций к вашим полям из-за ошибки Джексона .

Если вы хотите проверить значения полей, вы должны сделать это в конструкторе не в строителе. Таким образом, вы также поймете те экземпляры, где строитель не используется. Для этого просто внедрите конструктор all-args самостоятельно; метод build() будет использовать его автоматически:

private AvailabilityResponse(List<Train> trainDetails, String name, Optional<String> detail) {
    this.trainDetails = Objects.requireNonNull(trainDetails);
    this.name = Objects.requireNonNull(name);
    this.detail = Objects.requireNonNull(detail);
}

Я предлагаю сделать этот конструктор private, потому что

  • заставляет пользователей вашего класса использовать конструктор, и
  • плохой стиль иметь Optional s в качестве параметров в вашем API Publi c.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...