做者:丁仪html
来源:https://chengxuzhixin.com/blog/post/Jackson-fan-xu-lie-hua-zi-dong-shi-pei-zi-lei.htmljava
json 格式使用很是方便,一般状况下咱们反序列化的时候须要指定具体类型。若是遇到继承类型可能会解析失败。今天总结下基于类型扩展的子类自动适配,可以实现反序列化时按需适配子类。算法
好比定义 Model 类型,仅有字段 key,类型为 String。默认状况下,json 里面只能配置 Model 已有的字段,只有相似这样的数据能够反序列化成功:json
{"key": "demo"}
若是 Model 是架构底层的定义,而且容许上层应用继承 Model 实现业务自定义字段,默认的解析就没法知足扩展需求了。或者 json 数据来自外部,内部须要路由以实现定制,也是没法知足的。好比,json 数据增长 value 字段,变成:架构
{"key": "demo","value": "test"}
一般的方案多是在 Model 增长 value 字段。对于架构设计和具备良好兼容性的代码来讲,增长 value 字段不是最合适的。在分层架构中,Model 可能位于底层,或者在引入的 jar 包中,业务没法直接修改字段定义。此时能够基于 Jackson 的子类适配能力,经过继承类型实现自定义字段的反序列化。app
咱们给 Model 类型增长一个字段 type,加上 Jackson 注解 JsonTypeInfo。在 JsonTypeInfo 中指定子类扩展的属性字段是 type,和 json 数据中的 type 字段对应。JsonTypeInfo 中的字段含义以下:异步
@Getter @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = Model.class, visible = true) class Model { @JsonIgnore private String type; private String key; }
增长一个 Model 的子类 CustomModel。由 CustomModel 类扩展 value 字段,实现业务扩展定制。这里在项目中增长一个 @JsonTypeDefine 注解来定义 CustomModel 是 Model 的名字为 custom 的子类型扩展。代码以下:post
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface JsonTypeDefine { String value() default ""; String desc() default ""; } @JsonTypeDefine("custom") class CustomModel extends Model { private String value; }
对于上面提到的 json 数据,增长一个 type 字段,值为 custom。运行时 Jackson 识别到 type 值为 custom ,就会按照 custom 关联的类型进行解析。基于这样的类型扩展,上层业务能够灵活定制,架构底层能够不感知上层定制。json 数据变动为:spa
{"type": "custom","key": "demo","value": "test"}
要想让 Jackson 认识 custom 这个名字,须要在系统初始化的时候,扫描到全部的子类定义,并注入到 Jackson 中。以下代码实现了对类型扩展的扫描和注入。主要分为几个步骤架构设计
// 使用开源库 Reflections 扫描 JsonTypeInfo 定义的基类 Set<Class<?>> types = reflections.getTypesAnnotatedWith(JsonTypeInfo.class); // 遍历基类 for (Class<?> type : types) { // 使用开源库 Reflections 扫描子类 Set<?> clazzs = reflections.getSubTypesOf(type); if(CollectionUtils.isEmpty(clazzs)){ continue; } // 注册子类,demo 代码,请自行修改 for (Class<?> clazz : clazzs) { // 跳过接口和抽象类 if(clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())){ continue; } // 提取 JsonTypeDefine 注解 JsonTypeDefine extendClassDefine = clazz.getAnnotation(JsonTypeDefine.class); if (extendClassDefine == null) { continue; } // 注册子类型,使用名称创建关联 objectMapper.registerSubtypes(new NamedType(clazz, extendClassDefine.value())); } }
通过以上的系统初始化,Jackson 就已经可以识别 Model 类型的名字为 custom 的子类型了。在解析时无需特别处理,直接调用 Jackson 的反序列化方法便可实现解析。对如下数据的解析,将直接转换成 CustomModel 类型:
{"type": "custom","key": "demo","value": "test"}
推荐阅读