I'm trying to deseralize JSON I'm getting:
[
{
"Name":"0",
"Health":0,
"TypeName":"SpellInfo",
"Info":{
"Effect":1,
"EffectAmount":4
}
},
{
"Name":"1",
"Health":0,
"TypeName":"MonsterInfo",
"Info":{
"Health":10,
"AttackDamage":10
}
},
...
...
]
Created a class to handle the JSON:
[System.Serializable]
public class CardDataStructure
{
public string Name;
public int Health;
public string TypeName;
public Info Info;
}
I managed to get all the info I needed but the Info. From the research I did, I created a JsonConverter
from a link - https://blog.codeinside.eu/2015/03/30/json-dotnet-deserialize-to-abstract-class-or-interface/
Which is actually pretty close,
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;
}
}
(It would have been better to find it by 'typename' but I couldn't figure out how to do that, so went with something simple)
When checking 'jo', the properties are there and go to the correct class yet once out of the converter I get default properties and not the once the converter showed.
I can't find the link but on the Newtonsoft doc it said somewhere there's a problem with deserializing an abstract class and if the abstract class doesn't have a public setter.
Both monsterinfo and spellinfo inherit from info:
[Serializable]
public abstract class Info
{
}
The monsterinfo
and spellinfo
look basically the same. Problem is they don't have a public setters and I cannot change them right now.
{
[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;
}
}
So, when trying to deseralize the 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 is a list of CardDataStructure
Is it even possible to get the data in Info without giving them a public setter? Best I got is all the data inside the JSON and an empty Monster/Spell Info.
At the end I just need to parse the json I'm getting, but while the 'name', 'health', 'typeinfo' are parsed correctly, info is always an empty object filled with 0s.
Edit: Corrected some things.
You should do that like this dude:
A marker interface for detecting the type or deserializing
A container 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();
}
}
And you should execute:
List<Card<Info>> list = JsonConvert.DeserializeObject<List<Card<Info>>>(jsonText, new InfoConverter());