使用摘要 属性 JSON 反序列化 class

Deserializing class with abstract property JSON

public abstract class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        [JsonProperty(Required = Required.Always)]
        public string Type { get; set; }
    }

    public class Employee : Person
    {
        public string Department { get; set; }
        public string JobTitle { get; set; }
    }

    public class Artist : Person
    {
        public string Skill { get; set; }
    }

我已经有一个 JSON 转换器根据类型 属性 的值反序列化此类对象。

public abstract class JsonCreationConverter<T> : JsonConverter
    {
        protected abstract T Create(Type objectType, JObject jObject);

        public override bool CanConvert(Type objectType)
        {
            Type t = typeof(T);
            return typeof(T).IsAssignableFrom(objectType);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject jObject = JObject.Load(reader);
            T target = Create(objectType, jObject);
            serializer.Populate(jObject.CreateReader(), target);
            return target;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }

    public class PersonConverter : JsonCreationConverter<Person>
    {
        protected override Person Create(Type objectType, JObject jObject)
        {
            if(jObject["Type"] == null)
            {
                throw new ArgumentException();
            }

            string type = (string)jObject["Type"];
            if(type == null)
            {
                throw new ArgumentException();
            }

            if(type.Equals("Employee", StringComparison.InvariantCultureIgnoreCase))
            {
                return new Employee();
            }
            else if (type.Equals("Artist", StringComparison.InvariantCultureIgnoreCase))
            {
                return new Artist();
            }

            return null;
        }
    }

string json = "[{\"Department\": \"Department1\",\"JobTitle\": \"JobTitle1\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Employee\"},{\"Skill\": \"Drawer\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Artist\"}]";
List<Person> person = JsonConvert.DeserializeObject<List<Person>>(json, new PersonConverter());

上面的效果很好。

现在,我有以下 class:

public class City
    {
        public string Name { get; set; }
        public int Population { get; set; }
        public Person[] Persons { get; set; }
    }

如何为这个城市 class 编写一个可以使用 PersonConverter 来初始化 Persons 属性 的转换器?我最初的想法是将 Persons 部分提取为 JObject,然后在其 ReadJson 方法中使用 PersonConverter 对其调用反序列化,类似于下面的示例。

 var p = jObject["Persons"].ToString();
 List<Person> persons = JsonConvert.DeserializeObject<List<Person>>(p, new PersonConverter());

但是 ReadJson 在 Serializer.Populate 方法中抛出异常,因为抽象 classes 无法实例化。

以下以JSON字符串为例:

string Cityjson = "{\"Name\": \"London\" , \"Population\": \"1000\" , \"Persons\": [{\"Department\": \"Department1\",\"JobTitle\": \"JobTitle1\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Employee\"},{\"Skill\": \"Drawer\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Artist\"}]}";

方法 #1

我通过

解决了这个问题
  1. 标记要在反序列化中忽略的人 属性

    [JsonIgnore] public 人[] 人 { get;放; }

  2. Create方法中,实例化City对象,使用PersonConverter初始化Persons属性

    受保护的重写 City Create(Type objectType, JObject jObject) { if (jObject["Persons"] == null) { 抛出新的 ArgumentException(); }

             var p = jObject["Persons"].ToString();
    
             List<Person> persons = JsonConvert.DeserializeObject<List<Person>>(p, new PersonConverter());
             var city = new City();
             city.Persons = persons.ToArray();
             return city;
         }
    

ReadJson 方法将像往常一样填充剩余的 City 属性。

还有其他方法吗?

我认为这是最合适的方式

在 ReadJson 中,传递数组时它基本上崩溃了,因为 Jarray 不是 jboject。因此,我按如下方式更新了 ReadJson 并且它起作用了。

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.StartArray)
            {
                JArray jObject = JArray.Load(reader);
                List<T> list = new List<T>();
                for (int i = 0; i < jObject.Count(); i++)
                {
                    var p = jObject[i];
                    JObject ob = p as JObject;
                    T value = Create(objectType, ob);
                    serializer.Populate(ob.CreateReader(), value);
                    list.Add(value);
                }

                return list.ToArray();
            }
            else
            {
                JObject jObject = JObject.Load(reader);
                T target = Create(objectType, jObject);
                serializer.Populate(jObject.CreateReader(), target);
                return target;
            }
        }

是的,我不需要 CityConverter。添加 PersonConverter 就足够了。