一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Jackson自定义反序列化器遭遇类型不匹配的典型问题

时间:2026-06-01 08:25:01 编辑:袖梨 来源:一聚教程网

Jackson框架中自定义String反序列化时,若忽略token类型校验会导致类型不匹配错误被静默处理为null值。下面通过具体案例揭示这一隐蔽陷阱。

背景

在 Jackson 中自定义 String 反序列化逻辑时,若未正确校验 token 类型,将导致类型不匹配错误被静默处理为 null 值而非抛出异常。

Jackson 自定义反序列化器的类型不匹配陷阱

代码

SimpleStringDeserializer.java

public class SimpleStringDeserializer extends JsonDeserializer {    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        // TODO
        // if (p.currentToken() != JsonToken.VALUE_STRING) {
        //     throw MismatchedInputException.from(p, String.class, "...");
        // }
        String value = p.getValueAsString();
        if (value == null) {
            return null;
        }
        return p.getValueAsString();
    }
}

Main.java

public class Main {    public static void main(String[] args) throws Exception {
        testWithoutDeserializer();
        testWithDeserializer();
    }    static void testWithoutDeserializer() {
        ObjectMapper mapper = new ObjectMapper();        System.out.println("=== Without SimpleStringDeserializer ===n");        String[] cases = {
                "{"name": [1,2]}",
        };        for (String json : cases) {
            System.out.println("Input: " + json);
            try {
                User user = mapper.readValue(json, User.class);
                System.out.println("Result: " + user.getName());
            } catch (Exception e) {
                System.out.println("Error: " + e.getClass().getSimpleName());
            }
            System.out.println();
        }
    }    static void testWithDeserializer() {
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule("SimpleStringModule");
        module.addDeserializer(String.class, new SimpleStringDeserializer());
        mapper.registerModule(module);        System.out.println("=== With SimpleStringDeserializer ===n");        String[] cases = {
                "{"name": [1,2]}",
        };        for (String json : cases) {
            System.out.println("Input: " + json);
            try {
                User user = mapper.readValue(json, User.class);
                System.out.println("Result: " + user.getName());
            } catch (Exception e) {
                System.out.println("Error: " + e.getClass().getSimpleName());
            }
            System.out.println();
        }
    }    static class User {
        private String name;        public String getName() { return name; }
        public void setName(String name) { 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 类型,保证行为与默认反序列化器一致

通过对比实验证实,自定义反序列化器必须严格校验token类型,才能避免静默处理类型错误的风险,确保与Jackson原生行为保持一致。

热门栏目