Jackson - 将 JSON 反序列化为具有可选字段的对象

问题描述 投票:0回答:1

我有一个

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
}
java json jackson deserialization option-type
1个回答
1
投票

不要使用Optional作为字段

我建议放弃可选。

首先,

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

如果您想不惜一切代价使用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
© www.soinside.com 2019 - 2024. All rights reserved.