system.text.Json 針對繼承多型型別的集合,使用自定義Converter,進行json序列化

HelloLLLLL發表於2024-09-23
測試類:
[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與物件轉換的方法,不過那樣我感覺不太好。針對繼承型別的集合序列化,反序列化,子類欄位不缺失,值也不缺失。

相關文章