作者:丁仪

来源: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. tfrecords转np.array

    import tensorflow as tf import numpy as np from keras.utils import to_categorical import sys def tfr ...

  2. 设计模式六大原则 All In one

    设计模式六大原则 All In one 开闭原则: 对扩展开放,对修改关闭; 设计模式的六大原则: 0.总原则-开闭原则 对扩展开放, 对修改封闭; 在程序需要进行拓展的时候, 不能去修改原有的代码, ...

  3. js currying & js 科里化

    js currying & js 科里化 var test = ( function (a){ console.log(`a2 =`, a);// 1 // console.log(`b2 = ...

  4. py python-pptx 创建ppt

    创建一个简单的PPTX文件 from pptx import Presentation class Main(): def __init__(self): prs = Presentation() t ...

  5. django学习-1.开始hello world!

    1.前言 当你想走上测试开发之路,用python开发出一个web页面的时候,需要找一个支持python语言的web框架.django框架有丰富的文档和学习资料,也是非常成熟的web开发框架,想学pyt ...

  6. Python 股票市场分析实战

    目标: 1.股票数据获取 2.历史趋势分析及可视化 3.风险分析 实验数据:来源于Yahoo Finance / Stooq,该网站提供了很多API接口,本文用的工具是pandas-datareade ...

  7. 01_MySQL从下载—>安装—>到快速上手

    一.MySQL下载 二.MySQL安装 三.MySQL几条简单命令快速上手(增删改查) 一.MySQL下载与安装 下载地址:https://dev.mysql.com/downloads/mysql/ ...

  8. 1071 Speech Patterns——PAT甲级真题

    1071 Speech Patterns People often have a preference among synonyms of the same word. For example, so ...

  9. webpack理解

    打包工具 可以将多个静态文件打包成一个静态文件例如将1.js.2.js.3.css打包成h.js一个静态文件 这样做的好处是:可以减少页面的请求次数 以往是请求多个静态页面,使用webpack后会减少 ...

  10. 模拟web服务器 (小项目) 搭建+部署

    模拟web服务器:可以从浏览器中访问到自己编写的服务器中的资源,将其资源显示在浏览器中. 技术选型: corejava 线程池 同任务并发执行 IO流 传递数据 客户端也会像服务端发送数据, 服务器像 ...