作者:丁仪

来源:https://chengxuzhixin.com/blog/post/Jackson-fan-xu-lie-hua-zi-dong-shi-pei-zi-lei.html

json 格式使用非常方便,通常情况下我们反序列化的时候需要指定具体类型。如果遇到继承类型可能会解析失败。今天总结下基于类型扩展的子类自动适配,能够实现反序列化时按需适配子类。

比如定义 Model 类型,仅有字段 key,类型为 String。默认情况下,json 里面只能配置 Model 已有的字段,只有类似这样的数据可以反序列化成功:

{"key": "demo"}

  

如果 Model 是架构底层的定义,并且允许上层应用继承 Model 实现业务自定义字段,默认的解析就无法满足扩展需求了。或者 json 数据来自外部,内部需要路由以实现定制,也是无法满足的。比如,json 数据增加 value 字段,变成:

{"key": "demo","value": "test"}

  

通常的方案可能是在 Model 增加 value 字段。对于架构设计和具有良好兼容性的代码来说,增加 value 字段不是最合适的。在分层架构中,Model 可能位于底层,或者在引入的 jar 包中,业务无法直接修改字段定义。此时可以基于 Jackson 的子类适配能力,通过继承类型实现自定义字段的反序列化。

我们给 Model 类型增加一个字段 type,加上 Jackson 注解 JsonTypeInfo。在 JsonTypeInfo 中指定子类扩展的属性字段是 type,和 json 数据中的 type 字段对应。JsonTypeInfo 中的字段含义如下:

  • use:指定用哪种方式自动适配子类,这里设为子类的名称;
  • property:指定配置子类型的字段,这里设为 type 字段;
  • defaultImpl:未设置 type 时默认的解析类型,这里设为 Model 本身;
  • visible:反序列化时 property 配置的字段是否解析出值放在结果中,默认是 false;
@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 的子类型扩展。代码如下:

@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 数据变更为:

{"type": "custom","key": "demo","value": "test"}

  

要想让 Jackson 认识 custom 这个名字,需要在系统初始化的时候,扫描到所有的子类定义,并注入到 Jackson 中。如下代码实现了对类型扩展的扫描和注入。主要分为几个步骤

  1. 扫描 JsonTypeInfo 定义的基类,如 Model,这里采用开源库 Reflections 实现;
  2. 扫描子类,如 CustomModel,也采用开源库 Reflections 实现;
  3. 注册子类,从 JsonTypeDefine 注解中提取子类名称,如把 "custom" -> CustomModel 关系注入 Jackson。这里调用 Jackson 的 objectMapper 注册子类型方法 registerSubtypes 注入类型扩展 。
// 使用开源库 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"}

  

推荐阅读

SpringMVC异步处理的 5 种方式

Linux Cron 定时任务

人类简史、软件架构和中台

限流算法探秘

三十而立,如期而至

10 分钟轻松学会 Jackson 反序列化自动适配子类的更多相关文章

  1. 10分钟轻松学会python turtle绘图

     1. 画布(canvas) 1.1 相关函数: 2. 画笔 2.1 画笔的状态 2.2 画笔的属性 2.3 绘图命令 3. 命令详解 4. 绘图举例 4.1 太阳花 4.2 绘制小蟒蛇 4.3 绘 ...

  2. 10分钟轻松学会 Python turtle 绘图

    python2.6版本中后引入的一个简单的绘图工具,叫做海龟绘图(Turtle Graphics),turtle库是python的内部库,使用导入即可 import turtle 先说明一下turtl ...

  3. 10分钟轻松设置出 A+ 评分的 HTTP/2 网站

    前言 其实 HTTP/2 应该是 2015 年的老话题了(2015 年 5 月 14 日 HTTP/2 协议正式版的发布),但是 2018 年都到了很多网站依旧没有使用,作为新一代互联网协议,HTTP ...

  4. 10 个轻松学会 CSS3 的优秀在线资源

    本文包揽 CSS 的所有关键点,并且引入了最新的 CSS3 版本.这个先进的技术提供超级多的新标签和属性,使得 Web 设计构建创新更简单,帮助开发者创建具有新趋势,带有漂亮布局的 Web 页面.随着 ...

  5. 使用 Chrome 浏览器插件 Web Scraper 10分钟轻松实现网页数据的爬取

    web scraper 下载:Web-Scraper_v0.2.0.10 使用 Chrome 浏览器插件 Web Scraper 可以轻松实现网页数据的爬取,不写代码,鼠标操作,点哪爬哪,还不用考虑爬 ...

  6. 在net中json序列化与反序列化 面向对象六大原则 (第一篇) 一步一步带你了解linq to Object 10分钟浅谈泛型协变与逆变

    在net中json序列化与反序列化   准备好饮料,我们一起来玩玩JSON,什么是Json:一种数据表示形式,JSON:JavaScript Object Notation对象表示法 Json语法规则 ...

  7. UWP开发入门(十九)——10分钟学会在VS2015中使用Git

    写程序必然需要版本控制,哪怕是个人项目也是必须的.我们在开发UWP APP的时候,VS2015默认提供了对微软TFS和Git的支持.考虑到现在Git很火,作为微软系的程序员也不得不学一点防身,以免被开 ...

  8. 如何从40亿整数中找到不存在的一个 webservice Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库 WPF实战案例-打印 RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

    如何从40亿整数中找到不存在的一个 前言 给定一个最多包含40亿个随机排列的32位的顺序整数的顺序文件,找出一个不在文件中的32位整数.(在文件中至少确实一个这样的数-为什么?).在具有足够内存的情况 ...

  9. 【云开发】10分钟零基础学会做一个快递查询微信小程序,快速掌握微信小程序开发技能(轮播图、API请求)

    大家好,我叫小秃僧 这次分享的是10分钟零基础学会做一个快递查询微信小程序,快速掌握开发微信小程序技能. 这篇文章偏基础,特别适合还没有开发过微信小程序的童鞋,一些概念和逻辑我会讲细一点,尽可能用图说 ...

随机推荐

  1. Apple WWDC All In One

    Apple WWDC All In One https://developer.apple.com/wwdc20/ https://developer.apple.com/videos/wwdc202 ...

  2. ES6 Arrow Function All In One

    ES6 Arrow Function All In One this const log = console.log; const arrow_func = (args) => log(`arg ...

  3. Taro 版本

    Taro 版本 https://taro-docs.jd.com/taro/versions.html 1.x 1.3.34 https://taro-docs.jd.com/taro/docs/1. ...

  4. 比特币跌破3.5万美元,巨鲸们将目光瞄向SPC算力币

    比特币最近又迎来了大幅下跌,截至周三(1月20日),比特币跌幅超过5%,跌破3.5万美元.很显然,比特币没有预期那样顺顺利利地登顶4万美元,反而又出现了回调迹象.有些巨鲸们在大肆囤币,然而也有些巨鲸们 ...

  5. 关于USDN你想知道的一切

    稳定币的发展只有短短几年的时间.稳定币最早起源于 2014 年 Bitfinex 组建的泰德公司发行的 USDT.稳定币是一种具有稳定价值的加密货币.稳定币诞生的背景是加密货币的价格波动巨大,其作为一 ...

  6. 200万枚SPC空投来袭,这样的薅羊毛活动你确定不参加吗?

    在过去的2020年,币圈真的是很火爆,很多人在参与数字货币交易或DeFi挖矿中赚到了大钱.但是转眼到了2021年,DeFi进入了下半场,区块链市场也进入了新的阶段,那么区块链的下一个爆点是什么呢?很多 ...

  7. 【从零开始撸一个App】Fragment和导航中的使用

    Fragment简介 Fragment自从Android 3.0引入开始,它所承担的角色就是显而易见的.它之于Activity就如html片段之于页面,好处无需赘述. Fragment的生命周期和Ac ...

  8. .NET 云原生架构师训练营(模块二 基础巩固 安全)--学习笔记

    2.8 安全 认证 VS 授权 ASP .NET Core 认证授权中间件 认证 JWT 认证 授权 认证 VS 授权 认证是一个识别用户是谁的过程 授权是一个决定用户可以干什么的过程 401 Una ...

  9. Spring的BeanFactoryPostProcessor接口

    接口简介 BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点,Spring IoC 容器允许 BeanFactoryPostPr ...

  10. 如何将文件夹取消svn关联

    随便在什么目录新建一个文本文件,文件名随便,将文本文件打开,将下面的文字复制到文本文件中: Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHI ...