In order to deserialize derived object it is possible to use CustomCreationConverter and Discriminators. So I managed to get this working for the SkillEffect base class but now I need the same approach for a Skill base class as well. How can I use the SkillEffectConverter within the SkillConverter when deserializing a ProjectileSkill? If this is not possible with Newtonsoft, are there other libraries approaches to achieve this?
internal class Program
{
static void Main(string[] args)
{
part1();
part2();
}
static void part1()
{
SkillEffect resourceEffect = new ResourceSkillEffect(3);
var json = JsonConvert.SerializeObject(resourceEffect, new SkillEffectConverter());
Console.WriteLine(json);
var effect = JsonConvert.DeserializeObject<SkillEffect>(json, new SkillEffectConverter());
effect.Apply();
}
static void part2()
{
Skill projSkill = new ProjectileSkill();
projSkill.effects.Add(new ResourceSkillEffect(1));
projSkill.effects.Add(new ResourceSkillEffect(2));
var json = JsonConvert.SerializeObject(projSkill, new SkillConverter());
Console.WriteLine(json);
var skill = JsonConvert.DeserializeObject<Skill>(json, new SkillConverter());
Console.WriteLine(skill);
Console.Read();
}
}
public abstract class Skill
{
public abstract SkillDiscriminator Type { get; }
public List<SkillEffect> effects = new();
}
public class ProjectileSkill : Skill
{
public override SkillDiscriminator Type => SkillDiscriminator.ProjectileSkill;
}
public enum SkillDiscriminator
{
ProjectileSkill
}
public class SkillConverter : CustomCreationConverter<Skill>
{
private SkillDiscriminator _currentObjectType;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jobj = JObject.ReadFrom(reader);
_currentObjectType = jobj["Type"].ToObject<SkillDiscriminator>();
return base.ReadJson(jobj.CreateReader(), objectType, existingValue, serializer);
}
public override Skill Create(Type objectType)
{
return _currentObjectType switch
{
SkillDiscriminator.ProjectileSkill => new ProjectileSkill(),
_ => throw new NotImplementedException(),
};
}
}
public abstract class SkillEffect
{
public abstract SkillEffectDiscriminator Type { get; }
public abstract void Apply();
}
public class ResourceSkillEffect : SkillEffect
{
public override SkillEffectDiscriminator Type => SkillEffectDiscriminator.ResourceSkillEffect;
public ResourceSkillEffect() { }
public ResourceSkillEffect(int value)
{
this.value = value;
}
public int value;
public override void Apply()
{
Console.WriteLine($"ResourceSkillEffect: value={value}");
}
}
public enum SkillEffectDiscriminator
{
ResourceSkillEffect
}
public class SkillEffectConverter : CustomCreationConverter<SkillEffect>
{
private SkillEffectDiscriminator _currentObjectType;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jobj = JObject.ReadFrom(reader);
_currentObjectType = jobj["Type"].ToObject<SkillEffectDiscriminator>();
return base.ReadJson(jobj.CreateReader(), objectType, existingValue, serializer);
}
public override SkillEffect Create(Type objectType)
{
return _currentObjectType switch
{
SkillEffectDiscriminator.ResourceSkillEffect => new ResourceSkillEffect(1),
_ => throw new NotImplementedException(),
};
}
}
Edit 1: I updated the code with a full example. Function part1() works as expected and part2() crashes as expected.
I just saw that I can pass multiple converters to the JsonConvert.DeserializeObject
function which works.
var skill = JsonConvert.DeserializeObject<Skill>(
json,
new SkillConverter(),
new SkillEffectConverter());