You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 15:07:38