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 定時任務

人類簡史、軟件架構和中台

限流算法探秘

三十而立,如期而至


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM