简体   繁体   English

如何使用 lombok builder 反序列化 Java 可选字段?

[英]How to deserialize Java Optional field with lombok builder?

I want to deserialize Java Optional field in lombok builder.我想反序列化 Lombok 构建器中的 Java 可选字段。 Below is my code下面是我的代码

@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
        }
    }   

}    

If I override the builder method like below, able to deserialize如果我像下面这样覆盖构建器方法,则能够反序列化

    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;
        }   

I have used setterPrefix ="with" in @Builder.我在@Builder 中使用了 setterPrefix ="with"。 But if I override the above method with "with" prefix, its not working.但是如果我用“with”前缀覆盖上述方法,它就不起作用。

Please someone help me to acheive this请有人帮我实现这个

Jackson supports Optional using its jackson-datatype-jdk8 module. Jackson 支持Optional使用其jackson-datatype-jdk8模块。 You can simply add it to the <dependencies> section in your pom.xml :您可以简单地将其添加到pom.xml<dependencies>部分:

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

Then, initialize your mapper as follows:然后,按如下方式初始化您的映射器:

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

That mapper will automatically detect Optional s: If the XML field has a value, it will be wrapped in an Optional ;该映射器将自动检测Optional s:如果 XML 字段有值,它将被包装在Optional中; if the XML field is empty (ie, <DETAIL_TRAIN/> ), then the result will be an Optional.empty() .如果 XML 字段为空(即<DETAIL_TRAIN/> ),则结果将为Optional.empty() (In your case you have an attribute; those cannot be empty.) (在您的情况下,您有一个属性;那些不能为空。)

Jackson maps fields or attributes that do not exist in the XML to null (or, more precisely, it simply does not call the setter, so the field value will still be the default). Jackson 将 XML 中不存在的字段或属性映射到null (或者更准确地说,它根本不调用设置器,因此字段值仍然是默认值)。 If you also want to have an empty Optional in that case, you need to set the field default to Optional.empty() and add a @Builder.Default to that field.如果在这种情况下您还希望有一个空的Optional ,则需要将字段默认设置为Optional.empty()并将@Builder.Default添加到该字段。

Finally, lombok can automatically copy your annotations from the fields to the generated builder.最后,lombok 可以自动将您的注释从字段复制到生成的构建器。 You need to tell lombok which annotations to copy using a lombok.config file in your project root containing:您需要使用项目根目录中的lombok.config文件告诉 lombok 要复制哪些注释:

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

This means you do not need to customize your builder class at all:这意味着您根本不需要自定义构建器 class:

@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();
}

Note that you also need @JsonProperty annotations on your fields due to a Jackson bug .请注意,由于Jackson 错误,您还需要在字段上@JsonProperty注释。

If you want to validate field values, you should do that in the constructor, not in the builder.如果要验证字段值,则应在构造函数中进行,而不是在构建器中进行。 In this way you'll also catch those instantiations where the builder is not used.通过这种方式,您还将捕获那些不使用构建器的实例。 To do so, simply implement the all-args constructor yourself;为此,只需自己实现全参数构造函数; the build() method will use it automatically: 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);
}

I suggest making this constructor private , because我建议将此构造函数private ,因为

  • it enforces users of your class to use the builder, and它强制 class 的用户使用构建器,并且
  • it is bad style to have Optional s as parameters in your public API.在公共 API 中使用Optional作为参数是不好的风格。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM