1

I'm in the process of learning C# and the main way I've been doing so is trying to make a terminal application RPG game.

I planned on using JSON files as my save files and data files (for storing game states as well as items + their stats.)

My implementation of saving the current gamestate to a JSON file works perfectly fine:

public static void writeJson(string name, float xp, float resource, string weapon, string scene, string _class) {
            
            var saveFile = new saveGame(name, xp, resource, weapon, scene, _class) {

                playerName = name,
                playerExp = xp,
                resourceAmt = resource,
                weaponName = weapon,
                currentScene = scene,
                playerClass = _class

            };

            string fileName = $"{name}-{scene}-{_class}-SaveGame.json";
            var options = new JsonSerializerOptions { WriteIndented = true };
            string jsonString = JsonSerializer.Serialize(saveFile, options); // create the json data string
            
            File.WriteAllText(fileName, jsonString); // write the data to a file and name it accordingly
            Console.WriteLine(File.ReadAllText(fileName)); // for debug purposes only

            // move file to saves folder
            string sourceFile = System.IO.Path.Combine(mainPath, fileName);
            string destFile = System.IO.Path.Combine(savesPath, fileName);
            System.IO.File.Move(sourceFile, destFile, true);
            
        }

which is then later called at:

public void selectClass() {

            // get character name
            Console.WriteLine("Enter your character's name:");
            string charName = Console.ReadLine();
            // get class
            Console.WriteLine("Choose your class:");
            Console.WriteLine("Mage\nRogue\nDuelist\nRanger");
            classIn = Console.ReadLine();
            userClass = classIn.ToLower();

            int randomNum = numGenerator(); // call rng method

            switch (userClass) {
                case "mage":
                    Mage playerMage = new Mage(charName, randomNum); // call new mage constructor
                    
                    // print a line to the user describing the new Mage they have just created.
                    Console.WriteLine($"{playerMage.mageName}, your new Mage, has just been created.\nThey have {playerMage.Mana} mana points, and are using {playerMage.rngWeaponNameMage} as their weapon of choice.");
                    string startingScene = "Awakening";
                    string classID = "Mage";
                    saveGame initialSave = new saveGame(playerMage.mageName, playerMage.mXP, playerMage.Mana, playerMage.rngWeaponNameMage, startingScene, classID);

                    saveGame.writeJson(playerMage.mageName, playerMage.mXP, playerMage.Mana, playerMage.rngWeaponNameMage, startingScene, classID);
                    
                    break;

which outputs a JSON that looks like:

{
  "playerName": "test09",
  "playerExp": 0,
  "currentScene": "Awakening",
  "weaponName": "Wand of Air",
  "resourceAmt": 10,
  "playerClass": "Mage"
}

but when I try the reverse and attempt to load said save files with this code:

public static string[] readJson() {
            
            if (System.IO.Directory.Exists(savesPath)) {
                
                // TODO: need a way to index the strings being printed so only the name of the file is displayed, not the entire path.
                string[] dirFiles = System.IO.Directory.GetFiles(savesPath);
                int i = 0;
                foreach (string s in dirFiles) {
                    Console.WriteLine($"{i+1}. {dirFiles[i]}");
                    i++;
                }

                Console.WriteLine("Enter the number of the save file you'd like to load:\n");
                
                // take user input to select the file and index accordingly
                int saveFileChosen = Convert.ToInt32(Console.ReadLine());
                Console.WriteLine(saveFileChosen);
                string tempFileName = dirFiles[saveFileChosen]; 
                string fileName = tempFileName.Remove(0,44); // temporary hard coded file pathname trimming
                Console.WriteLine(fileName);

                // move the save file to main folder so it can be accessed.
                string sourceFile = System.IO.Path.Combine(savesPath, fileName);
                string destFile = System.IO.Path.Combine(mainPath, fileName);

                System.IO.File.Move(sourceFile, destFile, true);

                string jsonString = File.ReadAllText(fileName);
                LoadGame loadGame = JsonSerializer.Deserialize<LoadGame>(jsonString);

             
                    
                string savedCharName = loadGame.playerName;
                float savedXP = loadGame.playerExp;
                string savedScene = loadGame.currentScene;
                string savedWeapon = loadGame.weaponName;
                float savedResource = loadGame.resourceAmt;
                string savedClass = loadGame.playerClass;

                string[] loadedFile = new string[6];
                loadedFile[0] = savedCharName;
                loadedFile[1] = Convert.ToString(savedXP);
                loadedFile[2] = savedScene;
                loadedFile[3] = savedWeapon;
                loadedFile[4] = Convert.ToString(savedResource);
                loadedFile[5] = savedClass;

                // move the file back to the saves folder
                System.IO.File.Move(destFile, sourceFile, true);
                return loadedFile;
            }
            else {
                Console.WriteLine($"Error, Save folder path: {savesPath} does not exist.");
                return null;
            }
        }

I get this error that catches on line: "LoadGame loadGame = JsonSerializer.Deserialize(jsonString);"

Exception has occurred: CLR/System.InvalidOperationException An unhandled exception of type 'System.InvalidOperationException' occurred in System.Text.Json.dll: 'Each parameter in constructor 'Void .ctor(System.String, Single, Single, System.String, System.String, System.String)' on type 'TextBasedRPG.LoadGame' 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.' at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ConstructorParameterIncompleteBinding(ConstructorInfo constructorInfo, Type parentType) at System.Text.Json.Serialization.Converters.ObjectWithParameterizedConstructorConverter1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) at System.Text.Json.Serialization.JsonConverter1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value) at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonConverter jsonConverter, Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state) at System.Text.Json.JsonSerializer.ReadCore[TValue](Utf8JsonReader& reader, Type returnType, JsonSerializerOptions options) at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, Type returnType, JsonSerializerOptions options) at System.Text.Json.JsonSerializer.Deserialize[TValue](String json, JsonSerializerOptions options) at TextBasedRPG.LoadGame.readJson() in C:\Users\natha\Documents\TextBasedRPG\Program.cs:line 143 at TextBasedRPG.Program.menuSelect() in C:\Users\natha\Documents\TextBasedRPG\Program.cs:line 462 at TextBasedRPG.Program.Main(String[] args) in C:\Users\natha\Documents\TextBasedRPG\Program.cs:line 432

I'm not quite sure how to proceed, as some of the other StackOverflow threads I've viewed have made me more confused, and the documentation doesn't seem to do anything different than I'm doing here (albeit, my case is slightly more complicated than the example given in the documentation).

The readJson method never has a "saveFile" variable initialized like the writeJson method does, and I was thinking this may be what's causing the issue but I'm not quite sure how I would resolve that. My understanding of the scenario was that the JsonSerializer.Deserialize would take the JSON file contents and convert them into something usable for me, which I would then have to assign to variables with types within my code. This is clearly incorrect; but I was wondering if replacing "JsonSerializer.Deserialize< LoadGame >" with "JsonSerializer.Deserialize< saveFile >", where saveFile was a var (like in the writeJson method), that took in the 6 variables as parameters and assigned them to variables with their corresponding correct types.

ie.

var saveFile = new loadGame(playerName, playerExp, currentScene, weaponName, resourceAmt,   playerClass) {

                string savedCharName = playerName;
                float savedXP = playerExp;
                string savedScene = currentScene;
                string savedWeapon = weaponName;
                float savedResource = resourceAmt;
                string savedClass = playerClass;

            };
LoadGame loadGame = JsonSerializer.Deserialize<saveFile>(jsonString);

I don't know if that will solve my issue or not, but I will leave this issue for now and wait to hopefully hear back from some people with suggestions/solutions!

Link to the other stackoverflow post I read

Link to the documentation page I was viewing

Thanks in advance for anyone who's able to help me out.

2 Answers2

1

Your problem can occur when Deserialize() cannot create a new instance of the object your are deserializing. In order to make Deserialize() work properly, make sure your object's properties has a getter and a setter, and make sure that your object has a contructor that cant be called with no argument.

Papsta
  • 11
  • 1
0

Take a look at the link. It says parameters name at constructor should much json property names