Jackson ObjectMapper启用默认类型序列化后反序列化类路径不匹配报错
解决Jackson序列化反序列化类路径不匹配的问题
这个问题我太熟了!你遇到的是Jackson序列化时写入的类全名和当前反序列化用的类路径不匹配导致的找不到类错误。毕竟你序列化时用的是com.hello.www.domain.entities.foo.domain.impl.FProduct,现在反序列化的目标类却是com.hello.www.domain.entities.trip.dto.models.FProduct,Jackson自然找不到对应关系。下面给你几个实用的解决办法,按需选择:
方法一:给新类添加别名注解(推荐,若能修改新类)
如果可以修改目标类com.hello.www.domain.entities.trip.dto.models.FProduct,直接给它加上@JsonTypeName注解,指定序列化时的旧类路径作为别名,再注册到ObjectMapper即可:
// 新的FProduct类代码 import com.fasterxml.jackson.annotation.JsonTypeName; @JsonTypeName("com.hello.www.domain.entities.foo.domain.impl.FProduct") public class FProduct { // 你的类属性和方法 }
然后配置反序列化用的ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper(); // 保持你原来的默认类型配置 objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); // 注册类型模块,让Jackson识别别名 objectMapper.registerModule(new SimpleModule().registerSubtypes(FProduct.class)); // 现在就可以正常反序列化了 FProduct product = objectMapper.readValue(jsonString, FProduct.class);
这个方法最简洁,让Jackson直接把旧类名和新类绑定起来,逻辑清晰。
方法二:自定义类型解析器(无需修改类,适合无法改动目标类的场景)
如果不能修改新类,可以通过自定义TypeResolver来拦截类名解析,把旧类路径映射到新类:
import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.databind.deser.impl.TypeDeserializerBase; import com.fasterxml.jackson.databind.jsontype.TypeDeserializer; import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; import java.util.HashMap; import java.util.Map; ObjectMapper objectMapper = new ObjectMapper(); objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); // 添加类名映射规则 objectMapper.addMixIn(Object.class, ClassNameMappingMixIn.class); // 定义MixIn用于注入自定义类型解析逻辑 abstract class ClassNameMappingMixIn { @JsonTypeInfo( use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class", resolver = CustomTypeResolver.class ) abstract void dummy(); } // 自定义TypeResolver,存放类路径映射 static class CustomTypeResolver extends TypeResolverBuilder.DefaultTypeResolverBuilder { private final Map<String, Class<?>> classMappings = new HashMap<>(); public CustomTypeResolver() { super(JsonTypeInfo.Id.CLASS); // 添加上你的类路径映射:旧类名 -> 新类 classMappings.put("com.hello.www.domain.entities.foo.domain.impl.FProduct", com.hello.www.domain.entities.trip.dto.models.FProduct.class); // 有其他类需要映射的话继续添加即可 } @Override public TypeDeserializer buildTypeDeserializer(DeserializationConfig config, JavaType baseType, Collection<NamedType> subtypes) { return new CustomTypeDeserializer((AsPropertyTypeDeserializer) super.buildTypeDeserializer(config, baseType, subtypes), classMappings); } } // 自定义TypeDeserializer,替换类名 static class CustomTypeDeserializer extends AsPropertyTypeDeserializer { private final Map<String, Class<?>> classMappings; public CustomTypeDeserializer(AsPropertyTypeDeserializer src, Map<String, Class<?>> classMappings) { super(src, src.getPropertyName(), src.getTypeIdResolver()); this.classMappings = classMappings; } @Override protected JavaType _findDeserializer(DeserializationContext ctxt, String typeId) throws IOException { // 先检查是否有映射的类,有则用新类,否则按原逻辑处理 Class<?> mappedClass = classMappings.get(typeId); if (mappedClass != null) { return ctxt.constructType(mappedClass); } return super._findDeserializer(ctxt, typeId); } }
这个方法通过拦截Jackson的类型解析流程,悄悄把旧类名换成新类,完全不需要改动业务类,适合复杂场景。
方法三:临时应急——直接替换JSON字符串中的类路径
如果只是临时解决,或者涉及的类很少,可以直接在反序列化前修改JSON字符串里的旧类路径:
String originalJson = "{\"@class\":\"com.hello.www.domain.entities.foo.domain.impl.FProduct\",\"flightTrip\":{.....}}"; // 替换旧类路径为新的 String modifiedJson = originalJson.replace( "com.hello.www.domain.entities.foo.domain.impl.FProduct", "com.hello.www.domain.entities.trip.dto.models.FProduct" ); // 反序列化 FProduct product = objectMapper.readValue(modifiedJson, FProduct.class);
这个方法简单粗暴,但如果JSON里有很多嵌套类需要替换,或者类名出现在其他字段里,就容易出问题,只适合小范围临时使用。
内容的提问来源于stack exchange,提问作者unknown_boundaries
相关产品推荐
相关产品推荐

