我有一个
Optional<Double>
字段,我正在尝试从 JSON 反序列化该字段,但出现以下异常:
Cannot construct instance of `java.util.Optional`
(although at least one Creator exists):
no BigDecimal/double/Double-argument constructor/factory method
to deserialize from Number value (2.406)
我的班级:
public final class TestDims implements JsonTyped, Serializable {
private final Optional<Double> carMeters;
@JsonCreator
public TestDims(@JsonProperty("carMeters") Optional<Double> carMeters) {
this.carMeters = carMeters;
}
@JsonProperty("carMeters")
public Optional<Double> getCarMeters() {
return Optional.of(carMeters.orElse(2.2d));
}
}
示例 JSON:
{
"carMeters": 2.406
}
我建议放弃可选。
首先,
Optional
没有实现Serializable
,因此你的类是损坏的(因为对于可序列化的对象,整个对象图应该是可序列化的)。
其次,使用
Optional
作为字段是一种反模式,返回可选值的 getter 很不方便,因为它们不是暴露实际值,而是强制调用者处理可选值。
一个可能的解决方案是将
carMeters
的类型更改为 double
:
public final class TestDims implements JsonTyped, Serializable {
public static final double DEFAULT_METERS = 2.2;
private final double carMeters;
@JsonCreator
public TestDims(@JsonProperty("carMeters") double carMeters) {
this.carMeters = carMeters == 0 ? DEFAULT_METERS : carMeters;
}
@JsonProperty("carMeters")
public double getCarMeters() {
return carMeters;
}
}
main()
public static void main(String[] args) throws IOException {
String json1 = """
{
"carMeters": 2.406
}""";
String json2 = """
{
"carMeters": null
}""";
String json3 = """
{ }
""";
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(json1, TestDims.class));
System.out.println(mapper.readValue(json2, TestDims.class));
System.out.println(mapper.readValue(json3, TestDims.class));
}
输出:
TestDims{carMeters=2.406} // a regular value
TestDims{carMeters=2.2} // provided value is null
TestDims{carMeters=2.2} // the property is not present
如果您想不惜一切代价使用Optional作为字段,那么您的类将无法序列化。没有解决办法。
为了让 Jackson 知道如何处理Optional,你需要为 Jackson Datatype: JDK8 module 添加依赖,并注册它。
如果您没有使用任何框架,那么您需要在反序列化 JSON 之前使用
ObjectMapper.registerModule()
手动注册模块。
使用 Spring Boot,只需使用带有
@Bean
注解的方法将此模块添加到 Spring 的 Context 中就足够了。
@Bean
public Jdk8Module jdk8Module() {
return new Jdk8Module();
}
配置时会自动注册
ObjectMapper
。
另外注意,使用
Optional<Double>
没有意义,因为有一个类型 OptionalDouble
包装了一个原语。
public final static class TestDimsss implements JsonTyped {
public static final OptionalDouble DEFAULT_METERS = OptionalDouble.of(2.2D);
private final OptionalDouble carMeters;
@JsonCreator
public TestDimsss(@JsonProperty("carMeters") OptionalDouble carMeters){
this.carMeters = carMeters;
}
@JsonProperty("carMeters")
public OptionalDouble getCarMeters() {
return carMeters.isPresent() ? carMeters : DEFAULT_METERS;
}
@Override
public String toString() {
return "TestDimsss{" +
"carMeters=" + carMeters +
'}';
}
}
main()
public static void main(String[] args) throws IOException {
String json1 = """
{
"carMeters": 2.406
}""";
String json2 = """
{
"carMeters": null
}""";
String json3 = """
{ }
""";
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Jdk8Module());
System.out.println(mapper.readValue(json1, TestDims.class));
System.out.println(mapper.readValue(json2, TestDims.class));
System.out.println(mapper.readValue(json3, TestDims.class));
}
输出:
TestDimsss{carMeters=OptionalDouble[2.406]} // a regular value
TestDimsss{carMeters=OptionalDouble.empty} // provided value is null
TestDimsss{carMeters=OptionalDouble.empty} // the property is not present