今天遇到 json.net 反序列化的問題,
( json.net deserilize dirven class problem! )
前情提要:
開發 web, server 與 client 透過 json 格式傳遞資料,
server 端在定義 ViewMode 時,DataModel 可能是 ChildAModel 或 ChildBModel 類別,
以 OO多型 ( Polymorphism ) 的設計來說,
實作一個父類 ( BaseModel ) 讓 ChildAModel, ChildBModel 繼承,
這樣 ViewModel.DataModel 就可以指定 ChildAModel, ChildBModel 其中之一的資料格式
如下:
public class BaseModel
{
public int key { get; set; }
}
public class ChildAModel: BaseModel
{
public string Name { get; set; }
public int Age { get; set; }
}
public class ChildBModel : BaseModel
{
public string SomeProperty1 { get; set; }
public int SomeProperty2 { get; set; }
}
public class ViewModel
{
public BaseModel DataModel { get; set; }
}
問題:
依這樣的設計,
將 ViewModel 的資料轉成 JSON 拋給 Client 沒問題,
JavaScript 本身就是延展性高的語言,
但由 Client 再拋給 Server 時,
Json.Net 將 JSON 反序列成物件時,
問題來了!
ViewModel.DataModel 只會被反序列化成 BaseModel ,
其他資料全掉光了,
傷腦筋啦! 該如何是好呢?
Solution:
基本概念是靠實作 CustomCreationConverter
.Create
來通知 Json.Net 反序列化 T 時,
要轉成哪一種類別,但此 Create 原生方法卻沒有提供其他資訊來判斷該用哪種類別
example:
public interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
DateTime BirthDate { get; set; }
}
public class Employee : IPerson
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class PersonConverter : CustomCreationConverter
{
public override IPerson Create(Type objectType)
{
return new Employee();
}
}
發現有善心人士也遇到此問題,並公開解法
覆寫 JsonConverter,擴充 Create 方法
如下: (
參考 )
public abstract class JsonCreationConverter : JsonConverter
{
protected abstract T Create(Type objectType, JObject jsonObject);
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var target = Create(objectType, jsonObject);
serializer.Populate(jsonObject.CreateReader(), target);
return target;
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
再自訂 Json.Net Conveter,並依物件的 property 回覆類別,如下
public class ModelConverter : JsonCreationConverter
{
protected override BaseModel Create(Type objectType,
JObject jsonObject)
{
if (jsonObject["Name"] != null)
{
return new ChildAModel();
}
else if (jsonObject["SomeProperty1"] != null)
{
return new ChildBModel();
}
else
{
return new BaseModel();
}
}
}
最後反序列化的使用方式,如下:
var deserializedModel = JsonConvert.DeserializeObject<BaseModel>(json,
new ModelConverter ());
以上!