SpringBoot对ObjectMapper Bean反序列化配置项默认值的更改逻辑

3,053 阅读2分钟

在项目开发使用Jackson框架进行JSON字符串解析时,发现了一个奇怪的问题:

使用新创建的ObjectMapper对象执行反序列化操作,当JSON字符串某个属性在Java POJO对象中未定义时,会抛出com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException异常。

代码如下:

@Data
class Person {
    private String name;
    private Integer age;
}

@Test
void testParseAdditionalPropWithNewMapper() throws JsonProcessingException {
    String jsonString = "{\n" +
            "    \"name\": \"Alex\",\n" +
            "    \"age\": 18,\n" +
            "    \"gender\": \"MALE\"\n" +
            "}";
    Person person = new ObjectMapper().readValue(jsonString, Person.class);
}

控制台输出:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "gender" (class com.alex.Person), not marked as ignorable (2 known properties: "name", "age"])
 at [Source: (String)"{
    "name": "Alex",
    "age": 18,
    "gender": "MALE"
}"; line: 4, column: 16] (through reference chain: com.alex.Person["gender"])

	at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
        ...

通过查看源码,在com.fasterxml.jackson.databind.DeserializationFeature类中发现了FAIL_ON_UNKNOWN_PROPERTIES配置项: image.png

可以看到,这个配置项定义的就是反序列化时,当JSON字符串某个属性在Java POJO对象中未定义时,是否解析失败,并且默认值为true

然而,在使用SpringBoot默认声明的ObjectMapper Bean时,发现同样的场景反序列化竟然成功了!

@Data
class Person {
    private String name;
    private Integer age;
}

@SpringBootTest
@Slf4j
class JacksonParsingTests {

    @Autowired
    private ObjectMapper objectMapper;
    
    @Test
    void testParseAdditionalProp() throws JsonProcessingException {
        String jsonString = "{\n" +
                "    \"name\": \"Alex\",\n" +
                "    \"age\": 18,\n" +
                "    \"gender\": \"MALE\"\n" +
                "}";
        Person person = objectMapper.readValue(jsonString, Person.class);
        log.info("person: {}", person);
    }
}

控制台输出:

2021-08-13 16:53:04.464  INFO 2116 --- [           main] com.alex.JacksonParsingTests             : person: Person(name=Alex, age=18)

由此可以判定,一定是SpringBoot声明ObjectMapper Bean时,将FAIL_ON_UNKNOWN_PROPERTIES配置项的值设置为了false

通过Google搜索,发现了GitHub上的这个issue

原来,在spring-web模块的org.springframework.http.converter.json.Jackson2ObjectMapperBuilder类中,对FAIL_ON_UNKNOWN_PROPERTIES配置项的值进行了显式的改写:

// Any change to this method should be also applied to spring-jms and spring-messaging
// MappingJackson2MessageConverter default constructors
private void customizeDefaultFeatures(ObjectMapper objectMapper) {
    ...
    if (!this.features.containsKey(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) {
            configureFeature(objectMapper, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }
}

在SpringBoot的ObjectMapper Bean声明类org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration中,引用到了上述的Jackson2ObjectMapperBuilder类,进行了ObjectMapper Bean的构建,感兴趣的小伙伴可以自行查看相关源码。

那么,如果使用SpringBoot的ObjectMapper Bean时,该如何开启FAIL_ON_UNKNOWN_PROPERTIES配置项呢?

通过以下配置,就可以了:

spring.jackson.deserialization.fail-on-unknown-properties=true