Json.Text.Serializer throwing constructor error during deserialization

Thomas Long 20 Reputation points
2023-12-27T20:04:00.53+00:00

I'm using the Json.Text.Serializer to both serialize and deserialize data for a test game I'm writing. The serialization works perfectly. However, when I attempt to deserialize I get an error message.

'Each parameter in the deserialization constructor on type 'Game.MonsterTemplate' must bind to an object property or field on deserialization. Each parameter name must match with a property or field on the object. The match can be case-insensitive.'

However, if I go in and look at the json view of the jsonstring I'm viewing, it shows all the data as correct, so I know that the program is at least recognizing the data correctly.

I know it's passing back an empty field while I try to work out testing issues. I've also gotten rid of the options on indented saving for right now while I try to figure out the loading issues, though I'd like to reimplement it. I just don't understand why the constructors are doing this. Even in the save method I attempted to deserialize the data I just passed so I'm deserializing the data I just serialized and it still throws an error.

Any help at all would be greatly appreciated.

        public List<MonsterTemplate> LoadMonsterTemplates()
        {
            string fileName = "MonsterTemplates.json";
            string jsonString = File.ReadAllText(fileName);

            var weatherForecast = JsonSerializer.Deserialize<List<MonsterTemplate>>(jsonString);

            MessageBox.Show(weatherForecast[0].imageSource);
            List<MonsterTemplate> list = new List<MonsterTemplate>();
            return list;
        }
        public void SaveMonsterTemplates(List<MonsterTemplate> Mons)
        {
            var options = new JsonSerializerOptions { WriteIndented = true };

            string fileName = "MonsterTemplates.json";
            //string jsonString = JsonSerializer.Serialize(Mons, options);
            string jsonString = JsonSerializer.Serialize(Mons);
            File.WriteAllText(fileName, jsonString);
        }

    class MonsterTemplate
    {
        public string name { get; set; }
        public string imageSource { get; set; }
        public double attack { get; set; }
        public double critChance { get; set; }
        public double critDamage { get; set; }
        public int maxHealth { get; set; }
        public bool KO { get; set; }

        public List<Attack> attackList { get; set; }

        [JsonConstructor]
        public MonsterTemplate(string oName, string oImageSource,  int oMaxHealth, double oAttack, double oCritChance, double oCritDamage, List<Attack> attacks)
        {
            name = oName;
            imageSource = oImageSource;
            maxHealth = oMaxHealth;
            attack = oAttack;
            critChance = oCritChance;
            critDamage = oCritDamage;
            attackList = attacks;
            KO = false;
        }
    }
    internal class Attack
    {
        public string name { get; set; }
        public double damage { get; set; }
        public double variance { get; set; }

        [JsonConstructor]
        public Attack(string oName, double oDamage, double oVariance)
        {
            name = oName;
            damage = oDamage;
            variance = oVariance;
        }
    }

User's image

[{"name":"Slime","imageSource":"C:/Users/Chow/source/repos/Game/slime.png","attack":4,"critChance":0.3,"critDamage":1.5,"maxHealth":63,"KO":false,"attackList":[{"name":"Basic","damage":1,"variance":0.5},{"name":"Tackle","damage":1.2,"variance":0.3},{"name":"Slap","damage":0.8,"variance":0.4}]},{"name":"Golem","imageSource":"C:/Users/Chow/source/repos/Game/golem.png","attack":9,"critChance":0.2,"critDamage":2.5,"maxHealth":54,"KO":false,"attackList":[{"name":"Basic","damage":1,"variance":0.5},{"name":"Slam","damage":1.5,"variance":0.3}]},{"name":"Wolf","imageSource":"C:/Users/Chow/source/repos/Game/wolf.png","attack":6,"critChance":0.7,"critDamage":2,"maxHealth":32,"KO":false,"attackList":[{"name":"Basic","damage":1,"variance":0.5},{"name":"Bite","damage":1.2,"variance":0.8}]},{"name":"Dinosaur","imageSource":"C:/Users/Chow/source/repos/Game/dinosaur.jpg","attack":4,"critChance":0.3,"critDamage":1.5,"maxHealth":120,"KO":false,"attackList":[{"name":"Basic","damage":1,"variance":0.5},{"name":"Tackle","damage":1.2,"variance":0.3},{"name":"Slap","damage":0.8,"variance":0.4}]},{"name":"Devil","imageSource":"C:/Users/Chow/source/repos/Game/devil.png","attack":9,"critChance":0.2,"critDamage":2.5,"maxHealth":73,"KO":false,"attackList":[{"name":"Basic","damage":1,"variance":0.5},{"name":"Slam","damage":1.5,"variance":0.3}]},{"name":"Armadillo","imageSource":"C:/Users/Chow/source/repos/Game/armadillo.jpg","attack":6,"critChance":0.4,"critDamage":2,"maxHealth":36,"KO":false,"attackList":[{"name":"Basic","damage":1,"variance":0.5},{"name":"Bite","damage":1.2,"variance":0.8}]},{"name":"Squirrel","imageSource":"C:/Users/Chow/source/repos/Game/squirrel.jpg","attack":8,"critChance":0.8,"critDamage":1.2,"maxHealth":27,"KO":false,"attackList":[{"name":"Basic","damage":1,"variance":0.5},{"name":"Tail Whip","damage":0.6,"variance":1.2}]}]

Developer technologies | C#
0 comments No comments
{count} votes

Accepted answer
  1. P a u l 10,761 Reputation points
    2023-12-27T21:00:51.07+00:00

    This is because the names of your arguments on your [JsonConstructor]s don't match the names in your JSON. For example, the first argument of the MonsterTemplate constructor is oName, which doesn't match the name property in your JSON.

    You can get around the name clash by using:

    this.name = name;
    

    Instead of:

    name = oName;
    

    Or alternatively just delete your constructors as they don't appear to do anything other than mapping arguments to properties. KO on MonsterTemplate is a bool so it's default value will be false anyway.

    1 person found this answer helpful.
    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. Viorel 122.6K Reputation points
    2023-12-27T20:58:13.24+00:00

    I think that it will work if the constructors are removed. Otherwise, try these constructors:

    [JsonConstructor]
    public Attack( string name, double damage, double variance )
    {
        this.name = name;
        this.damage = damage;
        this.variance = variance;
    }
    . . .
    [JsonConstructor]
    public MonsterTemplate( string name, string imageSource, int maxHealth, double attack, double critChance, double critDamage, List<Attack> attackList )
    {
        this.name = name;
        this.imageSource = imageSource;
        this.maxHealth = maxHealth;
        this.attack = attack;
        this.critChance = critChance;
        this.critDamage = critDamage;
        this.attackList = attackList;
        KO = false;
    }
    

    In addition, the LoadMonsterTemplates function must return the deserialised list.

    1 person found this answer helpful.
    0 comments No comments

  2. Thomas Long 20 Reputation points
    2023-12-28T00:19:43.8433333+00:00

    Thank you both so much. I kept the constructors because I feel they make my main code so much cleaner but renamed my inputs like you said and it worked fine.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.