Search code examples
c#jsonunity-game-enginejson.net

Read json dictionary with multiple sub keys into unity c# script


I am trying to get my data from a json file into unity with a c# script. The goal is to move all players at the same time based on positional data, which i already have. I need to group the dictionaries within the dictionary before the update() function based on a key in a dictionary. I did the same thing with just players based on match time in seconds for a whole match. Here i can use "Groupby" to sort group the dictionaries within the dictionary based on the time. Problem with specific events is, that i have a key for the event and then within each event the match time in seconds, which can be double if the events overlap timewise.

So with that json format i can read the data properly:

{
    "__comment__": "This file contains event and positional information. Data generated on 2024-07-14 17:51:10. Version: 1.0.0.",
    "event_data": [
        {
            "player_name": "player1",
            "league_id": xxxx,
            "team": 2,
            "x": 26.68,
            "y": 9.179,
            "z": 1.6,
            "match_time_seconds_column": 1,
            "event_key": 1
        },
        {
            "player_name": "player2",
            "league_id": xxxx,
            "team": 2,
            "x": 19.109,
            "y": 9.189,
            "z": 1.6,
            "match_time_seconds_column": 1,
            "event_key": 1
        },

With that format I cannot:

{
    "__comment__": "This file contains event and positional information grouped by match time seconds. Data generated on 2024-07-14 17:51:10. Version: 1.0.0.",
    "event_data": {
        "1": {
            "1": [
                {
                    "player_name": "player1",
                    "league_id": xxxx,
                    "team": 2,
                    "x": 26.68,
                    "y": 9.179,
                    "z": 1.6,
                    "match_time_seconds_column": 1,
                    "event_key": 1
                },
                {
                    "player_name": "player2",
                    "league_id": xxxx,
                    "team": 2,
                    "x": 19.109,
                    "y": 9.189,
                    "z": 1.6,
                    "match_time_seconds_column": 1,
                    "event_key": 1
                },
                {
                    "player_name": "player3",
                    "league_id": xxx,
                    "team": 2,
                    "x": 23.204,
                    "y": 8.801,
                    "z": 1.6,
                    "match_time_seconds_column": 1,
                    "event_key": 1
                },.....

....
        "34": {
            "873": [
                {
                    "player_name": "player4",
                    "league_id": xxxx,
                    "team": 1,
                    "x": 39.344,
                    "y": 19.727,
                    "z": 1.6,
                    "match_time_seconds_column": 873,
                    "event_key": 34
                },

If anyone could help me how to read this json file with the sub keys "1": { "1": [...

I would really appreciate it. Could not find a solution yet.

What I did so far. I tried to read the file liks this:

using Newtonsoft.Json;
public class DataReader3 : MonoBehaviour
{...
 
    [System.Serializable]
    public class EventPositionalDataList_structure
    {
        public EventPositionalData_structure[] event_data; // Ensure this matches the JSON file key name on top
    }

    public EventPositionalDataList_structure myEventPositionalDataList_structure;

    [System.Serializable]
    public class RootObject
    {
        public string __comment__;
        public Dictionary<string, Dictionary<string, List<EventPositionalData>>> event_data;
    }
}

 void Start()
 {
// Deserialize JSON data for event and positional info
myEventPositionalDataList_structure = JsonUtility.FromJson<EventPositionalDataList_structure>(jsonFile_event_positional_info_structure.text);
RootObject rootObject = JsonConvert.DeserializeObject<RootObject>(jsonFile_event_positional_info_structure.text);

Until here i dont get any error. From now on i get following error: "error CS0029: Cannot implicitly convert type 'System.Collections.Generic.Dictionary<string, System.Collections.Generic.Dictionary<string,"

groupedEventData = rootObject.event_data;

        // Debug: Print out the deserialized event data
        foreach (var eventKey in groupedEventData.Keys)
        {
            Debug.Log($"Event Key: {eventKey}");
            foreach (var positionalKey in groupedEventData[eventKey].Keys)
Error:  error CS1061: 'List<DataReader3.EventPositionalData>' does not contain a definition for 'Keys' and no accessible extension method 'Keys' accepting a first argument of type 'List<DataReader3.EventPositionalData>' could be found (are you missing a using directive or an assembly reference?)
            {
                foreach (var positionalData in groupedEventData[eventKey][positionalKey])
                {
                    Debug.Log($"Player Name: {positionalData.player_name}, X: {positionalData.x}, Y: {positionalData.y}, Z: {positionalData.z}, Event Key: {positionalData.event_key}");
                }
            }
        }

Solution

  • First of all, I have to admit that I'm not familiar with unity, but a lot with json serialization in .net, so i created a sample console app to reproduce your error.

    To get your solution to work, i created some POCOs which represent the json structure that you provided:

    public class EventPositionalDataList
    {
        [JsonPropertyName("__comment__")]
        public string Comment { get; set; }
    
        [JsonPropertyName("event_data")]
        public Dictionary<string, Dictionary<string, List<EventPositionalData>>> EventData { get; set; }
    }
    
    public class EventPositionalData
    {
        [JsonPropertyName("player_name")]
        public string PlayerName { get; set; }
    
        //TODO add all properties
    }
    

    I added JsonPropertyNameAttributes to match the json properties that do not follow C# naming guidelines. They need a public setter to be populated by the JsonSerializer

    Then I can call the serializer like this:

    var json = File.ReadAllText("testdata.json");
    var myEventPositionalDataList = JsonSerializer.Deserialize<EventPositionalDataList>(json);
    
    var groupedEventData = myEventPositionalDataList.EventData;
    foreach (var eventKey in groupedEventData.Keys)
    {
        Console.WriteLine($"Event Key: {eventKey}");
        foreach (var positionalKey in groupedEventData[eventKey].Keys)
        {
            foreach (var positionalData in groupedEventData[eventKey][positionalKey])
            {
                Console.WriteLine($"Player Name: {positionalData.PlayerName}");
            }
        }
    }
    

    Your deserialization seemed already correct, I just changed the class definitions.

    Hope this helps. If you have further questions, dont hesitate to ask :)