1
votes

I am learning the Newtonsoft JSON library in C#.

I have a class called "enemy" that descends from the "Ente" class. I have a list of type "Ente". In this list I add some instances of the "enemy" class and serialize the list, all correct. Then, with the list empty, I read the JSON file from disk, deserialize it and assign the result to the list of type "Ente". All correct but ... enemy objects are created using the constructor of the class "Ente", this is the problem, the objects should be created by calling the constructor of the class "Enemy", that is to say, objects "Enemy" must be created and not "Ente" objects.

**Edit 1:

I have other classes that also descend from "Ente" and the instances must be saved in the "EnteList" list. For example, these are other classes that descend from "Ente":

Enemy: Ente

BallOfFire: Ente

Troll: Ente

etc...

In the example I simplified it to the maximum and thought that it was not necessary to give that information. I think the newtonsoft library should do this automatically because I am specifying to save the object type that is in the list in the JSON file when serializing it.**

The question is: What do I need to do to make the "Enemy" object constructor run at the time of deserialization?

List:

public List< Ente > EnteList { get; set; } = new List< Ente >();

Code serialization and deserialization:

        EnteList.Add( new Enemy( ) );
        JsonSerializerSettings setting = new JsonSerializerSettings();
        setting.TypeNameHandling = TypeNameHandling.Objects;
        string json = JsonConvert.SerializeObject( EnteList, Formatting.Indented, setting );
        EnteList.Clear();
        EnteList = JsonConvert.DeserializeObject< List< Ente > >( json );

Class Ente and Enemy:

public class Ente
{
    public Ente()
    {
        Console.WriteLine( "\n Constructor of Ente executed" );
    }
}

public class Enemy : Ente
{
    public Enemy()
    {
        Console.WriteLine( "\n Constructor of Enemy executed" );
    }
}
1
"I think the newtonsoft library should do this automatically because I am specifying to save the object type that is in the list in the JSON file when serializing it." - You haven't specified any settings when deserializing though. I don't know whether or not that's the problem, but it at least seems possible. - Jon Skeet
I am looking at the documentation but I have no idea what attribute or property I should configure. - IndieDev
Well can't you pass the same settings into the deserialize method that you passed into the serialize method? - Jon Skeet
Hey, I hadn't thought that, thanks, that solved part of the problem. - IndieDev

1 Answers

0
votes

EDITED

I've understood you.

try this:

class Program
{
    public static List<Ente> EnteList { get; set; } = new List<Ente>();

    static void Main(string[] args)
    {
        EnteList.Add(new Enemy() { EnemyName = "Frodo" });
        EnteList.Add(new Troll() { TrollName = "Bilbo" });
        JsonSerializerSettings setting = new JsonSerializerSettings();
        setting.TypeNameHandling = TypeNameHandling.Objects;
        string json = JsonConvert.SerializeObject(EnteList, Formatting.Indented, setting);
        EnteList.Clear();

        List<Ente> result = JsonConvert.DeserializeObject<List<Ente>>(json, new JsonEnteConverter());

        Console.ReadLine();
    }
}

public class Ente
{
    public virtual string Type { get; set; }

    public Ente()
    {
        Console.WriteLine("\n Constructor of Ente executed");
    }
}

public class Enemy : Ente
{
    public override string Type { get => "Enemy"; }

    public string EnemyName { get; set; }

    public Enemy() 
    {
        Console.WriteLine("\n Constructor of Enemy executed");
    }
}

public class Troll : Ente
{
    public override string Type { get => "Troll"; }

    public string TrollName { get; set; }

    public Troll()
    {
        Console.WriteLine("\n Constructor of Troll executed");
    }
}

public class JsonEnteConverter : Newtonsoft.Json.Converters.CustomCreationConverter<Ente>
{
    public override Ente Create(Type objectType)
    {
        throw new NotImplementedException();
    }

    public Ente Create(JObject jObject)
    {
        var type = jObject.Property("Type").ToString(); //get property Type from your json
        switch (type)
        {
            case "Enemy":
                return new Enemy();
            case "Troll":
                return new Troll();
        }
        throw new ApplicationException(String.Format("Type not found", type));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject jObject = JObject.Load(reader);
        var targetObject = Create(jObject); // Create your object based on JObject
        serializer.Populate(jObject.CreateReader(), targetObject);
        return targetObject;
    }
}

The main thing is - you have to write your custom JsonConverter