測試類:
[JsonConverter(typeof(PersonConverter))] public class Person { public string FirstName { get; set; } public string LastName { get; set; } } [JsonConverter(typeof(PersonConverter))] public class Employee : Person { public string Department { get; set; } public string JobTitle { get; set; } } [JsonConverter(typeof(PersonConverter))] public class Artist : Person { public string Skill { get; set; } }
自定義Converter:
public class PersonConverter : JsonConverter<Person> { public override Person Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { JsonDocument.TryParseValue(ref reader, out var doc); var elm = doc.RootElement; elm.TryGetProperty("isBase", out var jEl); elm.TryGetProperty("typeName", out var typeEl); elm.TryGetProperty("obj", out var objEl); var typeStr = typeEl.GetString(); var type = Type.GetType(typeStr); var objStr = objEl.GetRawText(); var dic = JsonSerializer.Deserialize<Dictionary<string, string>>(objStr); var obj = ToObject(dic, type); return obj as Person; } public override bool CanConvert(Type typeToConvert) { if (typeToConvert == typeof(Person)) return true; if (typeToConvert.BaseType == typeof(Person)) return true; return false; } public override void Write(Utf8JsonWriter writer, Person value, JsonSerializerOptions options) { var type = value.GetType(); var dic = new { typeName = type.FullName, obj = ToDictionary(value), }; JsonSerializer.Serialize(writer, dic, options); } public Dictionary<string, object> ToDictionary(object obj) { if (obj == null) throw new ArgumentNullException(nameof(obj)); var dictionary = new Dictionary<string, object>(); foreach (var property in obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (property.CanRead) { dictionary[property.Name] = property.GetValue(obj, null); } } return dictionary; } public T ToObject<T>(Dictionary<string, object> source) where T : class, new() { T result = new T(); Type type = typeof(T); foreach (KeyValuePair<string, object> item in source) { PropertyInfo property = type.GetProperty(item.Key); if (property != null && property.CanWrite) { property.SetValue(result, item.Value, null); } } return result; } public object ToObject(IDictionary<string, string> source, Type type) { var result = Activator.CreateInstance(type); foreach (KeyValuePair<string, string> item in source) { PropertyInfo property = type.GetProperty(item.Key); if (property != null && property.CanWrite) { property.SetValue(result, item.Value, null); } } return result; } }
入口方法:
static void Main(string[] args) { Console.WriteLine("Hello World!"); List<Person> people = new List<Person> { new Employee(){Department="test part", FirstName="Hello", LastName="L", JobTitle="engineer"}, new Person() { FirstName="ooook", LastName="is not", }, new Artist() { FirstName="oooo", LastName="is not ooo", Skill="coding" }, }; var str = JsonSerializer.Serialize(people); Console.WriteLine(str); var pes = JsonSerializer.Deserialize<List<Person>>(str); }
思路就是:轉換成特定的類,然後需要藉助Dictionary轉換,不然直接使用serialize會出現迴圈呼叫,或者直接再引用一個newTonsoft.json取代dictionnary與物件轉換的方法,不過那樣我感覺不太好。針對繼承型別的集合序列化,反序列化,子類欄位不缺失,值也不缺失。