如何在Jackson自定义反序列化器中调用默认反序列化器?
在自定义Jackson反序列化器中调用默认反序列化逻辑
我来帮你解决这个问题,在自定义Jackson反序列化器中调用默认反序列化逻辑其实很简单,但要注意几个关键细节,避免踩坑:
核心思路
不要手动新建ObjectMapper实例(这会绕过当前的Jackson配置,比如其他注解、注册的模块等),而是从Jackson的上下文(DeserializationContext)中获取原本会使用的默认反序列化器,然后调用它的deserialize方法。
具体实现方案
方案1:通过DeserializationContext动态获取默认反序列化器
这种方式不需要额外的注册逻辑,直接在自定义反序列化器中获取默认实现:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.node.TokenBuffer; import java.io.IOException; public class CustomDeserializer extends StdDeserializer<CustomObject> { public CustomDeserializer() { super(CustomObject.class); } @Override public CustomObject deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { // 1. 先判断触发自定义逻辑的条件 if (shouldUseCustomLogic(jp)) { // 自定义反序列化逻辑:使用当前上下文的codec解析,而非新建ObjectMapper JsonNode node = jp.getCodec().readTree(jp); CustomObject customObj = new CustomObject(); // 手动设置字段示例 customObj.setName(node.get("name").asText()); customObj.setAge(node.get("age").asInt()); return customObj; } else { // 2. 获取默认反序列化器并调用 JsonDeserializer<?> defaultDeserializer = ctxt.findRootValueDeserializer( ctxt.getTypeFactory().constructType(CustomObject.class) ); return (CustomObject) defaultDeserializer.deserialize(jp, ctxt); } } // 判断是否需要走自定义逻辑的方法,注意要重置JsonParser的位置 private boolean shouldUseCustomLogic(JsonParser jp) throws IOException { // 使用TokenBuffer保存当前的token流,避免解析后指针偏移 TokenBuffer buffer = new TokenBuffer(jp.getCodec(), false); buffer.copyCurrentStructure(jp); // 从buffer生成新的parser来判断条件 JsonParser tempParser = buffer.asParser(); tempParser.nextToken(); JsonNode node = tempParser.getCodec().readTree(tempParser); // 示例条件:如果存在"custom"字段且值为true,则走自定义逻辑 boolean useCustom = node.has("custom") && node.get("custom").asBoolean(); // 将原parser重置回初始位置 jp = buffer.asParser(); jp.nextToken(); return useCustom; } }
方案2:通过BeanDeserializerModifier注入默认反序列化器(更优雅)
这种方式会在注册自定义反序列化器时,直接拿到Jackson原本会使用的默认反序列化器,避免每次调用时动态获取:
第一步:修改自定义反序列化器,添加带默认反序列化器的构造方法
public class CustomDeserializer extends StdDeserializer<CustomObject> { private final JsonDeserializer<?> defaultDeserializer; // 用于接收默认反序列化器的构造方法 public CustomDeserializer(JsonDeserializer<?> defaultDeserializer) { super(CustomObject.class); this.defaultDeserializer = defaultDeserializer; } @Override public CustomObject deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { if (shouldUseCustomLogic(jp)) { // 自定义逻辑(同方案1) JsonNode node = jp.getCodec().readTree(jp); CustomObject customObj = new CustomObject(); customObj.setName(node.get("name").asText()); customObj.setAge(node.get("age").asInt()); return customObj; } else { // 直接调用注入的默认反序列化器 return (CustomObject) defaultDeserializer.deserialize(jp, ctxt); } } // 判断条件的方法(同方案1) private boolean shouldUseCustomLogic(JsonParser jp) throws IOException { TokenBuffer buffer = new TokenBuffer(jp.getCodec(), false); buffer.copyCurrentStructure(jp); JsonParser tempParser = buffer.asParser(); tempParser.nextToken(); JsonNode node = tempParser.getCodec().readTree(tempParser); boolean useCustom = node.has("custom") && node.get("custom").asBoolean(); jp = buffer.asParser(); jp.nextToken(); return useCustom; } }
第二步:创建BeanDeserializerModifier来注册自定义反序列化器
import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; public class CustomDeserializerModifier extends BeanDeserializerModifier { @Override public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { // 只对CustomObject类替换反序列化器 if (beanDesc.getBeanClass() == CustomObject.class) { return new CustomDeserializer(deserializer); } return super.modifyDeserializer(config, beanDesc, deserializer); } }
第三步:将Modifier注册到ObjectMapper
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; public class Main { public static void main(String[] args) { ObjectMapper mapper = new ObjectMapper(); // 注册自定义的Modifier mapper.registerModule(new SimpleModule() { @Override public void setupModule(SetupContext context) { super.setupModule(context); context.addBeanDeserializerModifier(new CustomDeserializerModifier()); } }); // 后续使用mapper进行反序列化即可 } }
关键注意事项
- 绝对不要新建
ObjectMapper:新建实例会丢失当前所有的Jackson配置(比如自定义模块、注解处理、日期格式化等),必须使用jp.getCodec()或ctxt.getObjectMapper()获取上下文的mapper。 - 重置JsonParser的位置:在判断条件时,如果解析了JsonParser的内容,必须将其重置回初始位置,否则默认反序列化器会从当前指针位置开始读取,导致数据解析错误(使用
TokenBuffer是最可靠的方式)。
内容的提问来源于stack exchange,提问作者PDS
相关产品推荐
相关产品推荐

