在单元测试通过时,Spring Jackson ObjectMapper无法进行JSON反序列化

问题描述

我的Spring Web应用程序中有一个类:

@Value // or @Data Lombok
public class Bar {
    private final BigDecimal value;

    @JsonCreator
    public Bar(double value) {
        this.value = BigDecimal.valueOf(value);
    }
}

我写了一个通过的单元测试:

@Test
void test() throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    Bar bar = new Bar(12.34);
    assertEquals(mapper.readValue("12.34",Bar.class),bar);
}

但是当我向控制器发送POST时,它无法反序列化请求正文(将12.34序列化为Bar实例),并出现以下错误

JSON解析错误:无法构造com.example.demo.Bar的实例(尽管存在至少一个Creator):没有可以从Number值反序列化的double / Double参数构造函数/工厂方法(12.34);嵌套异常是com.fasterxml.jackson.databind.exc.MismatchedInputException:无法构造com.example.demo.Bar的实例(尽管至少存在一个Creator):没有用于从Number值反序列化的double / Double参数构造函数/工厂方法(12.34 )

如果删除@Value,它可以反序列化。更令人困惑的是,如果我手动添加构造函数(由@Value创建),它仍然可以工作。但是,如果我删除@JsonCreator,它可以再次反序列化。 我想念什么?

解决方法

@Jsoncreator应该与@JsonProperty结合使用,以指定如何反序列化JSON对象。因此,例如,如果您有一个类似JSON的rest控制器:

{
  "value": 123
}

您的构造函数应如下注释:

@JsonCreator
public Bar(@JsonProperty("value") double value) {
    this.value = BigDecimal.valueOf(value);
}

尽管这可能看起来是多余的,但想法是,如果您要反序列化的JSON与属性名称不匹配,则可以灵活得多。

因此,例如,如果您接收的对象仍然具有键值,但是您的类具有属性myValue,则将可以进行以下操作:

public class Example {
    private final BigDecimal myValue;

    @JsonCreator
    public Bar(@JsonProperty("value") double myValue) {
        this.myValue= BigDecimal.valueOf(myValue);
    }
}