在项目开发使用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配置项:
可以看到,这个配置项定义的就是反序列化时,当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