使用 Newtonsoft 反序列化只有 getter 的摘要 class

Deserialize an abstract class that has only a getter using Newtonsoft

我正在尝试反序列化 JSON 我得到:

[
{
    "Name":"0",
    "Health":0,
    "TypeName":"SpellInfo",
    "Info":{
      "Effect":1,
      "EffectAmount":4
    }
  },
  {
    "Name":"1",
    "Health":0,
    "TypeName":"MonsterInfo",
    "Info":{
      "Health":10,
      "AttackDamage":10
    }
  },
...
...
]

创建了一个 class 来处理 JSON:

    [System.Serializable]
    public class CardDataStructure
    {
        public string Name; 
        public int Health; 
        public string TypeName;
        public Info Info;
    }

我设法获得了我需要的所有信息,但 Info.根据我所做的研究,我从 link - https://blog.codeinside.eu/2015/03/30/json-dotnet-deserialize-to-abstract-class-or-interface/ 创建了一个 JsonConverter 这实际上非常接近,

public class InfoConvert: JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Info));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);

        if (jo.ToString().Contains("Effect"))
        {
            if (jo["Effect"].Value<string>() is string)
                return jo.ToObject<SpellInfo>(serializer);
        }
        if (jo.ToString().Contains("Health"))
        {
            if (jo["Health"].Value<string>() is string)
                return jo.ToObject<MonsterInfo>(serializer);
        }  
        return null;
        }
}

(最好通过 'typename' 找到它,但我不知道该怎么做,所以选择了一些简单的方法)
当检查 'jo' 时,属性在那里并转到正确的 class 但是一旦离开转换器我得到默认属性而不是一旦转换器显示。 我找不到 link 但在 Newtonsoft 文档上它说某处存在反序列化抽象 class 的问题,如果抽象 class 没有 public setter.

monsterinfo 和 spellinfo 都继承自 info:

    [Serializable]
    public abstract class Info
    {

    }

monsterinfospellinfo看起来基本一样。问题是他们没有 public setter,我现在不能更改它们。

{
    [Serializable]
    public class MonsterInfo: Info
    {
        [SerializeField] 
        private int m_Health;
        public int Health => m_Health;
        
        [SerializeField]
        private int m_AttackDamage;
        public int AttackDamage => m_AttackDamage;
        

    }
}

因此,在尝试反序列化 JSON 时:

string contents = File.ReadAllText(source);
contents = "{\"cards\":" + contents + "}";
JsonConverter[] converters = { new InfoConvert() };
cardsData = JsonConvert.DeserializeObject<Cards>(contents, new JsonSerializerSettings() {
Converters = converters, NullValueHandling = NullValueHandling.Ignore, 
TypeNameHandling = TypeNameHandling.Auto});

        

*Cards 是 CardDataStructure 的列表

是否有可能在不给它们 public setter 的情况下获取 Info 中的数据? 我得到的最好的是 JSON 中的所有数据和一个空的 Monster/Spell Info.

最后我只需要解析我得到的 json,但是当 'name'、'health'、'typeinfo' 被正确解析时,信息是总是一个用 0 填充的空对象。

编辑:更正了一些内容。

你应该像这个家伙那样做:

  • 用于检测类型或反序列化的标记接口

  • 一个容器class

  • Dto classes

     //marker interface
     public interface Info { }
    
     public class HealthInfo : Info
     {
         public int MoreHealth { set; get; }
         public int AttackDamage { set; get; }
     }
    
     public class SpellInfo : Info
     {
    
         public int Effect { set; get; }
    
         public int EffectAmount { set; get; }
     }
    
     public class Card<T> where T : Info
     {
         public Card(string name, int health, T info)
         {
             this.Info = info;
             this.Name = name;
             this.Health = health;
         }
    
         public T Info { private set; get; }
         public string Name { set; get; }
         public int Health { set; get; }
     }
    
     public class InfoConverter : JsonConverter
     {
         public override bool CanConvert(Type objectType)
         {
             if (objectType == typeof(Card<Info>))
             {
                 return true;
             }
    
             return false;
         }
    
         public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
         {
             JObject jObject = JObject.Load(reader);
             if (jObject.ContainsKey("TypeName"))
             {
                 string typeName = jObject["TypeName"]?.ToString()?.Trim()?.ToLower();
                 if (typeName?.Equals("monsterinfo") == true)
                 {
                     Card<HealthInfo> deseerialized = jObject.ToObject<Card<HealthInfo>>();
                     return new Card<Info>(deseerialized.Name, deseerialized.Health, deseerialized.Info);
                 }
                 if (typeName?.Equals("spellinfo") == true)
                 {
                     string json = jObject.ToString();
                     Card<SpellInfo> deseerialized = jObject.ToObject<Card<SpellInfo>>();
                     return new Card<Info>(deseerialized.Name, deseerialized.Health, deseerialized.Info);
                 }
             }
    
             return null;
         }
    
         public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
         {
             throw new NotImplementedException();
         }
     }
    

你应该执行:

List<Card<Info>> list = JsonConvert.DeserializeObject<List<Card<Info>>>(jsonText, new InfoConverter());