1

I'm working on a project where the API returns a JSON response with nested Array Objects. I've tried to loop through to get the inner most array of string (Hex) items. But the item shows as null.

Here is the JSON response: I'm trying to access the main part below, which is an array of string hex bytes.

{   // The main response
    "Success": true,
    "Total": 49,
    "TotalPages": 49,
    "ModesList": [
        {
            "Id": 1058,
            "Name": "kharabeesh",
            "Order": 0,
            "StepOneTime": "2.03s",
            "UserId": 1015,
            "IsPublic": true,
            "User": "Sahar M",
            "Date": "02/06/2021",
            "Mode": [ // I'm trying to access this part
                [
                    "16",
                    "1",
                    "1",
                    "0",
                    "5",
                    "71",
                    "255",
                    "255",
                    "255",
                    "76",
                    "20"
                ],
                [
                    "16",
                    "2",
                    "1",
                    "6",
                    "53",
                    "71",
                    "76",
                    "3",
                    "33",
                    "0",
                    "17"
                ],
                [
                    "16",
                    "3",
                    "1",
                    "6",
                    "53",
                    "71",
                    "73",
                    "127",
                    "87",
                    "0",
                    "17"
                ],
                [
                    "16",
                    "4",
                    "1",
                    "7",
                    "181",
                    "127",
                    "127",
                    "119",
                    "45",
                    "0",
                    "17"
                ],
                [
                    "16",
                    "5",
                    "1",
                    "0",
                    "7",
                    "192",
                    "170",
                    "48",
                    "11",
                    "0",
                    "32"
                ],
                [
                    "16",
                    "6",
                    "1",
                    "7",
                    "130",
                    "15",
                    "67",
                    "71",
                    "170",
                    "0",
                    "32"
                ],
                [
                    "16",
                    "7",
                    "1",
                    "0",
                    "67",
                    "8",
                    "206",
                    "45",
                    "195",
                    "0",
                    "68"
                ]
            ]
        }
    ]
}

Here is what I have so far:

public class GetModesResponse
    {
        public bool Success;
        public long Total;
        public long TotalPages;
        public List<ModesList>[] ModesList;
    }

    public class ModesList
    {
        public long Id;
        public string Name;
        public long Order;
        public string StepOneTime;
        public long UserId;
        public bool IsPublic;
        public string User;
        public string Date;
        public List<long>[] Mode;
    }

private void PopulateModes(GetModesResponse publicModes)
    {
        var allModes = publicModes.ModesList;
        var index = 0;
        foreach(var mode in allModes[0])
        {
            Debug.Log(mode.Name);
            var modeItem = Instantiate(ModeItemPrefab, ModeContainer.transform);
            var usedColors = new List<Color>();
            var steps = mode.Mode[0];
            Debug.Log(steps[0]);
            var stepColor = mainScript.GetColor((byte) steps[6], (byte) steps[7], (byte) steps[8], (byte) steps[9], 0);
            if (!usedColors.Contains(stepColor))
            {
                usedColors.Add(stepColor);
            }
            modeItem.Initialize(index, (int) mode.Id, mode.Name, mode.User, usedColors, this);
        }
    }

Update

This is how I call the populate function after getting the JSON from the API:

private IEnumerator GetPublicModes()
    {
        WWWForm form = new WWWForm();
        form.AddField("keyword", "");
        form.AddField("size", 1000);
        form.AddField("pageNo", 1);
        
        using (UnityWebRequest www = UnityWebRequest.Post(Manager.ApiUrlBase + "GetModes", form))
        {
            yield return www.SendWebRequest();
            if (!www.isDone)
            {
                Debug.Log(www.error);
            }
            else
            {
                var response = www.downloadHandler.text;
                Debug.Log("Get Modes complete!");
                GetModesResponse getModesResponse = new GetModesResponse();
                getModesResponse = JsonUtility.FromJson<GetModesResponse>(response);
                Debug.Log(getModesResponse.ToString());
                if (getModesResponse.Success)
                {
                    //Populate List
                    gottenModes = getModesResponse;
                    PopulateModes(getModesResponse);
                    
                }
                else
                {
                    // Display Error Message
                    Debug.Log(response);
                }
            }
        }
    }

UPDATE Solution: Thank you @MajidMohammadi, @AttilaMolnar, @A_____, and @derHugo for your help. @derHugo helped me find the answer by going to https://json2csharp.com/ it finally worked. Here is how I did it in the end:

[Serializable] 
    public class ModesList
    {
        public ModesList(
            int id,
            string name,
            int order,
            string stepOneTime,
            int userId,
            bool isPublic,
            string user,
            string date,
            List<List<string>> mode
        )
        {
            this.Id = id;
            this.Name = name;
            this.Order = order;
            this.StepOneTime = stepOneTime;
            this.UserId = userId;
            this.IsPublic = isPublic;
            this.User = user;
            this.Date = date;
            this.Mode = mode;
        }

        public int Id;
        public string Name;
        public int Order;
        public string StepOneTime;
        public int UserId;
        public bool IsPublic;
        public string User;
        public string Date;
        public List<List<string>> Mode;
    }

    [Serializable]
    public class GetModesResponse
    {
        public GetModesResponse(
            bool success,
            int total,
            int totalPages,
            List<ModesList> modesList
        )
        {
            this.Success = success;
            this.Total = total;
            this.TotalPages = totalPages;
            this.ModesList = modesList;
        }

        public bool Success;
        public int Total;
        public int TotalPages;
        public List<ModesList> ModesList;
    }
16
  • Have you tried change type of Mode property to the array of bytes(=byte[ ])? Commented Jun 4, 2021 at 8:48
  • No, I haven't, how would that look? Commented Jun 4, 2021 at 8:52
  • After I see exactly your code, I came to the conclusion that you might need change the type of Mode property to the type of byte[ ][ ](= array of byte array). I hope it resolve the problem. Commented Jun 4, 2021 at 8:57
  • Thanks you @Majid Mohammadi, but that didn't change anything unfortunatly. I'm still getting this error: NullReferenceException: Object reference not set to an instance of an object GalleryPanelScript.PopulateModes (GalleryPanelScript+GetModesResponse publicModes) (at Assets/Scripts/GalleryPanelScript.cs:83) which is this part of the code: foreach(var mode in allModes[0]) Commented Jun 4, 2021 at 9:01
  • 2
    I think, public List<ModesList>[] ModesList; should only be public List<ModesList> ModesList;, as ModeList is just a simple list not a nested list. And when you specified this in the other model public List<long>[] Mode; I am not sure it can be true because numbers are in apostrophes ("), so from JSON syntax view they must be string not long (unless if you do not have any conversion function for that record). I would specify it on this way: public List<List<string>> Mode; Commented Jun 4, 2021 at 9:02

2 Answers 2

1

In general for working with Unity your classes should be marked [Serializable]. See Unity Script Serialization for details.

And then what you have there in your JSON is not a List<long>[] but rather a List<string>[] or simply List<List<string>> since your numbers are wrapped in "!

So your class should look like e.g. (json2csharp often provides a very solid start point and requires only some little adoptions to work with the Unity serializer)

[Serializable]
public class ModesList
{
    public int Id;
    public string Name;
    public int Order;
    public string StepOneTime;
    public int UserId;
    public bool IsPublic;
    public string User;
    public string Date;
    public List<List<string>> Mode;
}

[Serializable]
public class GetModesResult
{
    public bool Success;
    public int Total;
    public int TotalPages;
    public List<ModesList> ModesList;
}

and then you would use byte.Parse like e.g.

var stepColor = mainScript.GetColor(byte.Parse(steps[6]), byte.Parse(steps[7]), byte.Parse(steps[8]), byte.Parse(steps[9]), 0);
      

However, only on the phone right now so can't test it but further

Note: Unity does not support serialization of multilevel types (multidimensional arrays, jagged arrays, and nested container types). If you want to serialize these, you have two options: wrap the nested type in a class or struct, or use serialization callbacks ISerializationCallbackReceiver to perform custom serialization. For more information, see documentation on Custom Serialization.

Sign up to request clarification or add additional context in comments.

2 Comments

thank you for your answer, but I then get the error: NullReferenceException: Object reference not set to an instance of an object. So how can I fix that?
When I loop through the ModesList.Mode and I put Debug.Log(step[0]) it prints in the console Null 7 times. So I'm unable to get the properties inside the list of Mode
0

I think you should change the structure of JSON.

I tested it locally:

The JSON file

{   
    "Success": true,
    "Total": 49,
    "TotalPages": 49,
    "ModesList": [
        {
            "Id": 1058,
            "Name": "kharabeesh",
            "Order": 0,
            "StepOneTime": "2.03s",
            "UserId": 1015,
            "IsPublic": true,
            "User": "Sahar M",
            "Date": "02/06/2021",
            "Mode": [
                {
                    "Value":
                    [   
                        "16",
                        "1",
                        "1",
                        "0",
                        "5",
                        "71",
                        "255",
                        "255",
                        "255",
                        "76",
                        "20"
                    ]   
                },
                {
                    "Value":
                    [    
                        "16",
                        "2",
                        "1",
                        "6",
                        "53",
                        "71",
                        "76",
                        "3",
                        "33",
                        "0",
                        "17"
                    ]   
                }
            ]
        }
    ]
}

The DataModuleClass(C#)


[Serializable]
public class XXXDataModule
{
    public bool Success;
    public int Total;
    public int TotalPage;

    public List<ModesList> ModesList;

    public XXXDataModule()
    {
    }

}


[Serializable]
public class ModesList
{
    public int Id;
    public string Name;
    public int Order;
    public string StepOneTime;
    public int UserId;
    public bool IsPublic;
    public string User;
    public string Date;
    public List<Mode> Mode;

    public ModesList()
    {
        
    }
}


[Serializable]
public class Mode
{
    public Mode()
    {
        
    }

    public List<string> Value;
}

The Deserialization(C#)


    public void OnClickJsonParse()
    {
        // I just simulated the reading of the data locally
        var jsonText = File.ReadAllText(Application.streamingAssetsPath + "/LitJson4Unity.json");
        Debug.Log("jsonText " + jsonText);

        //JsonData json = JsonMapper.ToObject(jsonText);

        // Use LitJson
        //XXXDataModule jsonData = JsonMapper.ToObject<XXXDataModule>(jsonText);

        // Use Unity JSONSerializeModule
        XXXDataModule jsonData = JsonUtility.FromJson<XXXDataModule>(jsonText);

        Debug.Log(jsonData.ModesList[0].Mode[0].Value[0]);

    }

VS Debug

2 Comments

When you have server APIs changing the JSON is often not an option though ;)
In my case, it might be difficult to change it

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.