如何使用 lombok 构建器反序列化 Java 可选字段?
How to deserialize Java Optional field with lombok builder?
我想反序列化 Java lombok 构建器中的可选字段。下面是我的代码
@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;
}
我在@Builder 中使用了setterPrefix ="with"。但是如果我用 "with" 前缀覆盖上面的方法,它就不起作用了。
请有人帮我实现这个
Jackson 使用其 jackson-datatype-jdk8
模块支持 Optional
。您只需将它添加到 pom.xml
:
中的 <dependencies>
部分
<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()
。 (在您的情况下,您有一个属性;这些属性不能为空。)
Jackson 将 XML 中不存在的字段或属性映射到 null
(或者更准确地说,它根本不调用 setter,因此字段值将仍然是默认值)。如果在这种情况下您还想有一个空的 Optional
,您需要将字段默认设置为 Optional.empty()
并向该字段添加一个 @Builder.Default
。
最后,lombok 可以自动将您的注释从字段复制到生成的构建器。您需要使用项目根目录中的 lombok.config
文件告诉 lombok 要复制哪些注释:
lombok.copyableAnnotations += com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty
config.stopBubbling = true
这意味着您根本不需要自定义构建器 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();
}
请注意,由于 a Jackson bug。
,您还需要在字段上添加 @JsonProperty
注释
如果你想验证字段值,你应该在构造函数中执行,而不是在生成器中。通过这种方式,您还将捕获未使用构建器的那些实例化。为此,只需自己实现全参数构造函数即可; 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
,因为
- 它强制您 class 的用户使用生成器,并且
- 在您的 public API.
中将 Optional
作为参数是错误的风格
我想反序列化 Java lombok 构建器中的可选字段。下面是我的代码
@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;
}
我在@Builder 中使用了setterPrefix ="with"。但是如果我用 "with" 前缀覆盖上面的方法,它就不起作用了。
请有人帮我实现这个
Jackson 使用其 jackson-datatype-jdk8
模块支持 Optional
。您只需将它添加到 pom.xml
:
<dependencies>
部分
<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()
。 (在您的情况下,您有一个属性;这些属性不能为空。)
Jackson 将 XML 中不存在的字段或属性映射到 null
(或者更准确地说,它根本不调用 setter,因此字段值将仍然是默认值)。如果在这种情况下您还想有一个空的 Optional
,您需要将字段默认设置为 Optional.empty()
并向该字段添加一个 @Builder.Default
。
最后,lombok 可以自动将您的注释从字段复制到生成的构建器。您需要使用项目根目录中的 lombok.config
文件告诉 lombok 要复制哪些注释:
lombok.copyableAnnotations += com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty
config.stopBubbling = true
这意味着您根本不需要自定义构建器 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();
}
请注意,由于 a Jackson bug。
,您还需要在字段上添加@JsonProperty
注释
如果你想验证字段值,你应该在构造函数中执行,而不是在生成器中。通过这种方式,您还将捕获未使用构建器的那些实例化。为此,只需自己实现全参数构造函数即可; 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
,因为
- 它强制您 class 的用户使用生成器,并且
- 在您的 public API. 中将
Optional
作为参数是错误的风格