JSON list file writing

So I’m trying to write a JSON file in the format of where X will increment every game (or another solution if it works):

{
    “gameX” : {
        “username” : “”,
        “timeToComplete” : “”,
        “levelUpgrade” : “”,
        “weaponUpgrade” : “”,
        “armourUpgrade” : “”,
        “totalMoney” : “”,
        “enemiesKilled” : “”,
        “gameCompleted” : “”
    }
}

Where this format will keep repeating for every game played. However, I’m not quite sure what is wrong with my code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEngine.SceneManagement;

public class Statistics : MonoBehaviour {

    public Player player;

    public GameManager manager;

    public EnemyDeathCounter enemyDeaths;

    static float timeCounter;

    static int totalSpent;
    
    string path = "Assets/Statistics.json";

    void Update() {
        totalTime();
        endGame();
    }

    void totalTime() {
        if(manager.gameInProgressInspector) {
            timeCounter += Time.deltaTime;
        }
    }

    void endGame() {
        if(player.playerDead == true || manager.gameCompleted) {
            // End game
            collectStatistics();
            SceneManager.LoadScene("Leaderboard");
        }
    }

    void collectStatistics() {
        statData statData = new statData();
        stats stats = new stats();
        string rawOldData = File.ReadAllText(path);
        statData oldData = JsonUtility.FromJson<statData>(rawOldData);
        stats.statDataList.Add(oldData);
        //statData.username //DO USERNAME INPUT IN MENU CLASS
        statData.timeToComplete = timeCounter;
        statData.playerLevel = player.playerLevel;
        statData.armourLevel = player.armourLevel;
        statData.weaponLevel = player.weaponLevel;
        statData.totalMoney = player.totalMoneySpent;
        statData.enemiesKilled = enemyDeaths.count;
        statData.gameCompleted = manager.gameCompleted;
        statData.score = completedScore(calculateScore(statData.timeToComplete, statData.playerLevel, statData.armourLevel, statData.weaponLevel, statData.totalMoney, statData.enemiesKilled), statData.gameCompleted);
        stats.statDataList.Add(statData);
        string json = JsonUtility.ToJson(stats.statDataList, true) + "

";
File.WriteAllText(path, json);
}

    int calculateScore(float timeToComplete, int playerLevel, int armourLevel, int weaponLevel, int totalMoney, int enemiesKilled) {
        if(totalMoney == 0 || enemiesKilled == 0) {
            // TotalMoney and/or enemiesKilled are 0 so need to be excluded
            int score = playerLevel * armourLevel * weaponLevel;
            if(totalMoney == 0 & enemiesKilled == 0) {
                // Both are 0
                score = (score * 100)/Mathf.RoundToInt(timeToComplete);
                return score;
            } else if(totalMoney == 0) {
                // Just totalMoney is 0
                score *= enemiesKilled;
                score = (score * 100)/Mathf.RoundToInt(timeToComplete);
                return score;
            } else if(enemiesKilled == 0) {
                // Just enemiesKilled is 0
                score *= totalMoney;
                score = (score * 100)/Mathf.RoundToInt(timeToComplete);
                return score;
            } else {
                return 0;
            }
        } else {
            // TotalMoney and enemiesKilled are both bigger than 0
            int score = playerLevel * armourLevel * weaponLevel * totalMoney * enemiesKilled;
            score = (score * 100)/Mathf.RoundToInt(timeToComplete);
            return score;
        }
    }

    int completedScore(int score, bool gameCompleted) {
        if(gameCompleted == true) {
            score *= 1;
            return score;
        } else {
            score *= 0;
            return score;
        }
    }

    [System.Serializable]
    public class statData {

        public string username;
        public float timeToComplete;
        public int playerLevel;
        public int armourLevel;
        public int weaponLevel;
        public int totalMoney;
        public int enemiesKilled;
        public bool gameCompleted;
        public int score;
    }

    [System.Serializable]
    public class stats {

        public List<statData> statDataList;
    }
}

Any help is greatly appreciated, thanks!

Your top element is an object. So your “gameX” would be a key from a key value pair. That key does not match any variable in your class. Instead you have a List in your stats class. The way you have setup your classes would produce json like this:

{
    "statDataList":[
        {
            "username":"some name",
            "timeToComplete":1234,
            ....
        },
        {
            "username":"some other name",
            "timeToComplete":1234,
            ....
        }
    ]
}

What you have shown can’t be modelled with Unity’s JsonUtility since a key of an object has to match an actual variable in the class that should represent that object. Of course you can not add variables dynamically.

If you really want to have a format like you’ve shown in your question, you can use my SimpleJSON parser. It does not map the json to actual C# classes but just represents the different json elements with generic classes.

To create a json string like you’ve shown you could simply do

var root = new JSONObject();
root["game1"]["username"] = "some name";
root["game1"]["timeToComplete"] = 1234;

root["game2"]["username"] = "some other name";
root["game2"]["timeToComplete"] = 1234;

string json = root.ToString(3);

Reading would work in a similar way.

var root = JSON.Parse(json);

string userName = root["game1"]["username"].Value;
float timeToComplete = root["game1"]["timeToComplete"].AsFloat;

It’s also possible to iterate over all key-value pairs like this:

foreach(var kv in root)
{
    Debug.Log("save name: " + kv.Key);
    Debug.Log("user name: " + kv.Value["username"].Value);
}

Note in many cases you can implicitly convert a JSONNode to string, float, int, etc. However in certain situations using the casting properties is required.