Jackson如何仅对Map指定条目实现自定义反序列化?
解决方案
针对你的需求,这里提供两种可行的实现方式:
方法一:直接遍历JSON树手动转换(简单直接)
不需要自定义反序列化器,先读取整个JSON为JsonNode,然后逐个处理键值对:指定的foo和bar直接保留为JsonNode,其他条目用默认逻辑转换为对应Object类型。
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.core.type.TypeReference; import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class Main { public static void main(String[] args) throws Exception { String json = "{\"someEntry\":\"ES\",\"foo\":[{\"a\":1},{\"b\":2}],\"bar\":{\"x\":\"y\"}}"; ObjectMapper mapper = new ObjectMapper(); // 读取整个JSON树 JsonNode root = mapper.readTree(json); Map<String, Object> result = new HashMap<>(); // 遍历所有键值对 Iterator<Map.Entry<String, JsonNode>> fieldIterator = root.fields(); while (fieldIterator.hasNext()) { Map.Entry<String, JsonNode> entry = fieldIterator.next(); String key = entry.getKey(); JsonNode value = entry.getValue(); if ("foo".equals(key) || "bar".equals(key)) { // 指定条目直接保留JsonNode result.put(key, value); } else { // 其他条目用默认逻辑反序列化 Object convertedValue = mapper.treeToValue(value, Object.class); result.put(key, convertedValue); } } // 验证结果 System.out.println(result.get("someEntry")); // 输出 ES(String类型) System.out.println(result.get("foo").getClass()); // 输出 class com.fasterxml.jackson.databind.node.ArrayNode System.out.println(result.get("bar").getClass()); // 输出 class com.fasterxml.jackson.databind.node.ObjectNode } }
方法二:自定义Map反序列化器(适合全局复用)
如果需要将该逻辑集成到ObjectMapper配置中,方便多次使用,可以自定义反序列化器,复用Jackson默认的Map反序列化逻辑,仅对指定键做特殊处理:
1. 自定义Map反序列化器
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectNode; import com.fasterxml.jackson.databind.deser.Deserializers; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import com.fasterxml.jackson.databind.module.SimpleModule; import java.io.IOException; import java.util.Map; class CustomMapDeserializers extends Deserializers.Base { @Override public JsonDeserializer<?> findMapDeserializer(MapType type, DeserializationConfig config, BeanDescription beanDesc, KeyDeserializer keyDeserializer, TypeDeserializer elementTypeDeserializer, JsonDeserializer<?> elementDeserializer) throws JsonMappingException { // 获取Jackson默认的Map反序列化器 JsonDeserializer<?> defaultDeserializer = super.findMapDeserializer(type, config, beanDesc, keyDeserializer, elementTypeDeserializer, elementDeserializer); // 返回自定义的反序列化器,持有默认实现 return new CustomMapDeserializer((JsonDeserializer<Map<String, Object>>) defaultDeserializer); } } class CustomMapDeserializer extends StdDeserializer<Map<String, Object>> { private final JsonDeserializer<Map<String, Object>> defaultDeserializer; protected CustomMapDeserializer(JsonDeserializer<Map<String, Object>> defaultDeserializer) { super(Map.class); this.defaultDeserializer = defaultDeserializer; } @Override public Map<String, Object> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { ObjectMapper mapper = (ObjectMapper) jsonParser.getCodec(); // 读取整个JSON树 ObjectNode rootNode = (ObjectNode) mapper.readTree(jsonParser); // 提取需要保留的节点 JsonNode fooNode = rootNode.remove("foo"); JsonNode barNode = rootNode.remove("bar"); // 将剩余节点交给默认反序列化器处理 Map<String, Object> result = mapper.treeToValue(rootNode, new TypeReference<Map<String, Object>>() {}); // 添加保留的JsonNode到结果Map if (fooNode != null) { result.put("foo", fooNode); } if (barNode != null) { result.put("bar", barNode); } return result; } }
2. 注册模块并使用
public class Main { public static void main(String[] args) throws Exception { String json = "{\"someEntry\":\"ES\",\"foo\":[{\"a\":1},{\"b\":2}],\"bar\":{\"x\":\"y\"}}"; // 注册自定义模块 SimpleModule module = new SimpleModule(); module.setDeserializers(new CustomMapDeserializers()); ObjectMapper mapper = new ObjectMapper().registerModule(module); // 反序列化 Map<String, Object> resultMap = mapper.readValue(json, new TypeReference<Map<String, Object>>() {}); // 验证结果 System.out.println(resultMap.get("someEntry")); System.out.println(resultMap.get("foo").getClass()); System.out.println(resultMap.get("bar").getClass()); } }
对之前问题的说明
- 第一种方案你遇到的问题是:没有复用默认的Map反序列化逻辑,反而尝试调用抽象父类的方法,这显然不可行。正确的做法是要么持有默认反序列化器,要么用
treeToValue转换剩余节点。 - 第二种方案的问题是:你声明反序列化器返回
Foo类型,但实际返回JsonNode,类型不匹配。既然Foo和Bar不在类路径中,完全不需要为它们写反序列化器,直接将对应节点保留为JsonNode即可。
内容的提问来源于stack exchange,提问作者IJR
相关产品推荐
相关产品推荐

