JSON 多型序列化紀錄

JSON polytype serialization, System.Text.Json, JsonElement, JsonSerializer

引言

工作紀錄以未來使用。

應用情境

流程圖(FlowDiagram)數據的序列化以存檔/載入的永續性應用。 FlowDiagram 內容物有 NodeList 與 LinkList 的集合。以程式碼表示如下:

class FlowDiagram { 
  public List<NodeBase> NodeList; // 多型集合
  public List<LinkBase> LinkList; // 也是多型集合
}

NodeBase只是基礎抽象類別,實體類別有:InitialNode,EndNode,ActivityNode等等。 LinkBase也是基礎抽象類別,實體類別有:GeneralLink, ConditionLink等等。

一些過程說明

預設不支援多型。不管是 JSON 還是 XML 都是不直接支援多型的。若有此需求一定有客製化或(不相同的)進階組態。近幾年都用 JSON 序列化早已確認不支援多型。以為 XML 序列化可以預設處理多型,花費約一個小時查找確定並不直接支援。

然若是有限度的多型序列化,只要一些客製化加工就能滿足需求。 本案例使用 System.Text.Json 實作。下面是案例說明。

關鍵說明-JSON多型序列化

經測試,JSON多型序列化是可以簡單做到的。只要改以"object"型別放置就可以"多型序列化"。上述案例改寫成:

class FlowDiagram { 
  public List<object> NodeList; // 多型集合
  public List<object> LinkList; // 也是多型集合
}

//# 以 object type 序列化就有『多型序列化』的效果 
FlowDiagram repo = new() { ... };
string json = JsonSerializer.Serialize(repo, options);

JSON 序列化時若發現來源是 object 時會用 GetType() 去取真實的 Type 再序列化,這樣等同多型序列化的效果。

關鍵說明-JSON多型反序列化

要進行反序列化有個關鍵之一就是要如何判定真實的 Type 為誰。簡單的方法就是在序列化時多填入它的 "Class" 識別名稱在之後反序列化時解開。反序列化成功的關鍵之二,若用 object type 解 JSON 封包就會解成 JsonElement type,這樣的好處是只要知到真實 Type 就能反序列化到其真實型別。

反序列化遇到object就會產生JsonElement實體,經判定出正確的型別再反序列化即可。

DiagramRepo derepo = JsonSerializer.Deserialize<DiagramRepo>(json);

//※ 反序列化遇到object就會產生JsonElement實體,經判定出正確的型別再反序列化即可。
for (int i = 0; i < derepo.NodeList.Count; i++)
{
  JsonElement element = (JsonElement)derepo.NodeList[i];
  
  // 取出預先標記的真實實體類別
  string nodeClass = element.GetProperty("Class").GetString(); 
  
  // 依正確的型別反序列化並回填。
  derepo.NodeList[i] = nodeClass switch
  {
    "Initial" => element.Deserialize<InitialNode>(),
    "End" => element.Deserialize<EndNode>(),
    "Activity" => element.Deserialize<ActivityNode>(),
    _ => element
  };
}

原始碼

參考文件

Last updated