• 10 分钟轻松学会 Jackson 反序列化自动适配子类


    作者:丁仪

    来源: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 定时任务

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

    限流算法探秘

    三十而立,如期而至

  • 相关阅读:
    花生壳 manjaro 安装
    manjaro+apache+django+mod_wsgi 安装
    arch linux或 Manjaro下安装 微信 wechat deepin-wine-wechat
    BBU+RRU基本介绍
    黑马python01——基础
    NNLearning阶段性总结01
    【信息论】——第二讲
    10.09——今日文章收集
    pygame安装【在pycharm的IDE project下】
    Git笔记——01
  • 原文地址:https://www.cnblogs.com/chengxuzhixin/p/14552182.html
Copyright © 2020-2023  润新知