Jackson 自定义反序列化器的类型不匹配陷阱
2026/6/8 7:49:27 网站建设 项目流程

背景

在 Jackson 中,当我们需要自定义String类型的反序列化逻辑时,通常会继承JsonDeserializer<String>并重写deserialize方法。但如果不注意 token 类型的校验,就会引入一个隐蔽的缺陷——类型不匹配被悄悄吞掉,变成 null

代码

SimpleStringDeserializer.java

publicclassSimpleStringDeserializerextendsJsonDeserializer<String>{@OverridepublicStringdeserialize(JsonParserp,DeserializationContextctxt)throwsIOException{// TODO// if (p.currentToken() != JsonToken.VALUE_STRING) {// throw MismatchedInputException.from(p, String.class, "...");// }Stringvalue=p.getValueAsString();if(value==null){returnnull;}returnp.getValueAsString();}}

Main.java

publicclassMain{publicstaticvoidmain(String[]args)throwsException{testWithoutDeserializer();testWithDeserializer();}staticvoidtestWithoutDeserializer(){ObjectMappermapper=newObjectMapper();System.out.println("=== Without SimpleStringDeserializer ===\n");String[]cases={"{\"name\": [1,2]}",};for(Stringjson:cases){System.out.println("Input: "+json);try{Useruser=mapper.readValue(json,User.class);System.out.println("Result: "+user.getName());}catch(Exceptione){System.out.println("Error: "+e.getClass().getSimpleName());}System.out.println();}}staticvoidtestWithDeserializer(){ObjectMappermapper=newObjectMapper();SimpleModulemodule=newSimpleModule("SimpleStringModule");module.addDeserializer(String.class,newSimpleStringDeserializer());mapper.registerModule(module);System.out.println("=== With SimpleStringDeserializer ===\n");String[]cases={"{\"name\": [1,2]}",};for(Stringjson:cases){System.out.println("Input: "+json);try{Useruser=mapper.readValue(json,User.class);System.out.println("Result: "+user.getName());}catch(Exceptione){System.out.println("Error: "+e.getClass().getSimpleName());}System.out.println();}}staticclassUser{privateStringname;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}}

运行结果

情况一:TODO 代码注释掉(不校验 token 类型)

=== Without SimpleStringDeserializer === Input: {"name": [1,2]} Error: MismatchedInputException === With SimpleStringDeserializer === Input: {"name": [1,2]} Result: null

情况二:TODO 代码未注释(校验 token 类型)

=== Without SimpleStringDeserializer === Input: {"name": [1,2]} Error: MismatchedInputException === With SimpleStringDeserializer === Input: {"name": [1,2]} Error: MismatchedInputException

结论

当 Jackson 解析到"name": [1,2]时,当前 token 是START_ARRAYgetValueAsString()对非字符串 token 的处理策略是返回 null 而非抛异常,所以自定义反序列化器把类型不匹配的情况悄悄吞掉了,变成了 null。而 Jackson 默认的 String 反序列化器内部会检查 token 类型,遇到START_ARRAY会主动抛出MismatchedInputException

这其实是自定义反序列化器的一个缺陷——应该在调用getValueAsString()之前先校验 token 类型,保证行为与默认反序列化器一致

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询