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

ObjectMapper处理Map<Range,List>序列化反序列化抛出ClassCastException

问题根因

你的问题既不是操作疏漏也不是框架Bug,本质是两个底层规则限制导致的:

  • JSON语法强制要求所有键必须为字符串类型:Jackson序列化非String类型的Map键时,会默认调用该对象的toString()方法转为字符串存储,你输出的序列化结果里的键就是Range对象的toString返回值,本身就不是结构化的Range对象。
  • 泛型擦除导致Jackson无法获取Map的真实泛型信息:你反序列化时直接传入Map.class作为目标类型,运行时泛型信息已经被擦除,Jackson不知道你需要把key反序列化为Range<Instant>类型,默认就会反序列化为String,你代码里声明的Map<Range<Instant>, List<String>>只是编译期的类型提示,对运行时的Jackson没有任何约束,因此取key时会触发类型转换异常。

你之前注册的GuavaModule只支持Range作为值的反序列化,没有提供Range作为Map键的反序列化逻辑,所以不会生效。

解决方案

需要两步操作修复:

  1. 自定义针对Range<Instant>的键反序列化器,适配你序列化生成的Range字符串格式
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.google.common.collect.Range;
import java.io.IOException;
import java.time.Instant;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RangeInstantKeyDeserializer extends KeyDeserializer {
    // 匹配你输出的闭区间Range字符串格式,如[2021-11-25T08:00:00Z..2021-11-26T08:00:00Z]
    private static final Pattern CLOSED_RANGE_PATTERN = Pattern.compile("\\[(.*?)\\.\\.(.*?)\\]");

    @Override
    public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
        Matcher matcher = CLOSED_RANGE_PATTERN.matcher(key);
        if (!matcher.matches()) {
            throw new IOException("无法识别的Range键格式: " + key);
        }
        Instant lowerBound = Instant.parse(matcher.group(1).trim());
        Instant upperBound = Instant.parse(matcher.group(2).trim());
        return Range.closed(lowerBound, upperBound);
    }
}
  1. 注册自定义反序列化器,反序列化时传入完整的泛型类型信息
// 原有模块注册逻辑保留
mapper.registerModule(new Jdk8Module()); 
mapper.registerModule(new JavaTimeModule());
mapper.registerModule(new GuavaModule());

// 注册自定义Range键反序列化器
SimpleModule customModule = new SimpleModule();
customModule.addKeyDeserializer(Range.class, new RangeInstantKeyDeserializer());
mapper.registerModule(customModule);

// 用TypeReference传递完整泛型信息,代替原来的Map.class
TypeReference<Map<Range<Instant>, List<String>>> typeRef = new TypeReference<Map<Range<Instant>, List<String>>>() {};
Map<Range<Instant>, List<String>> deserializedMap = mapper.readValue(serializedMap, typeRef);

修改完成后再执行取key的逻辑就不会抛出类型转换异常。

注意事项

如果你的业务场景中存在开区间、左开右闭、右开左闭等其他Range类型,只需要调整正则匹配规则,根据边界的括号/方括号标识调用对应Range.open/Range.closedOpen等构造方法即可。


内容的提问来源于stack exchange,提问作者S K

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 17:45:07