Jackson多态反序列化异常解决及正则匹配子类型实现问询
问题1 异常原因与修复方案
核心根因:你收到的JSON报文中data字段不是嵌套的JSON对象,而是被转义过的JSON字符串,Jackson默认不会自动对字符串类型的字段值做二次反序列化。
- 你使用
parser.getCodec().treeToValue(dataJson, type)时,dataJson本质是文本节点,Jackson尝试直接把字符串值转换为SeMessageData子类实例,自然会报错提示找不到对应类的字符串参数构造器 - 你修改后的写法先取出
data字段的字符串文本,再单独调用readValue做二次反序列化,完全匹配当前报文的结构,所以可以正常运行
如果想让最初的注解版代码正常运行,可以给SeMessage构造器的data参数加@JsonDeserialize注解,指定自定义反序列化器自动完成JSON字符串到对象的转换,也可以直接在ObjectMapper中注册全局的字符串转对象反序列化器适配该场景。
问题2 正则匹配子类型实现方案
Jackson原生的@JsonSubTypes.Type的name属性不支持正则匹配,但你可以通过自定义TypeIdResolver实现该需求,不需要写全量自定义反序列化器:
- 继承
TypeNameIdResolver类,重写typeFromId方法,在方法内将所有子类型的name作为正则规则,和传入的action值匹配,返回匹配成功的子类型 - 在
@JsonTypeInfo注解中指定using = 你自定义的Resolver类即可
示例实现参考:
public class RegexTypeIdResolver extends TypeNameIdResolver { protected RegexTypeIdResolver(MapperConfig<?> config, JavaType baseType, Collection<NamedType> subtypes, boolean forSer, boolean forDeser) { super(config, baseType, subtypes, forSer, forDeser); } @Override public JavaType typeFromId(DatabindContext context, String id) throws IOException { // 遍历所有注册的子类型,把name作为正则匹配传入的action值 for (Map.Entry<String, JavaType> entry : _idTypes.entrySet()) { if (id.matches(entry.getKey())) { return entry.getValue(); } } return super.typeFromId(context, id); } }
修改SeMessage中data字段的注解即可生效:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "action", include = JsonTypeInfo.As.EXTERNAL_PROPERTY) @JsonSubTypes({ @JsonSubTypes.Type(value = Se155QuestionsActiveMessageData.class, name = "^155-questions-active$"), @JsonSubTypes.Type(value = SeQuestionsMessageData.class, name = "^(?!155)\\d+-questions-(?:active|newest(?:-tag-[a-z0-9]+)?)$") }) @JsonTypeIdResolver(RegexTypeIdResolver.class) final SeMessageData data
内容的提问来源于stack exchange,提问作者Oliver
相关产品推荐
相关产品推荐

