2 lines of c# unity script to check.

Unity 2021.3

This:

var result = JsonUtility.FromJson<(string,string)[]>("{}");
Debug.Log($"Type: {result.GetType()} IsArray: {result.GetType().IsArray} Length: {result.Length}");

Gives me this

Type: System.ValueTuple`2[System.String,System.String][] IsArray: True Length: -854159105
UnityEngine.Debug:Log (object)

I might have some stupid mistake, but I’m looking at the code for a few minutes now and i cannot find it.

Is negative array length even possible.
I could not find anything like this in c# documentation.

Problems with Unity “tiny lite” built-in JSON:

In general I highly suggest staying away from Unity’s JSON “tiny lite” package. It’s really not very capable at all and will silently fail on very common data structures, such as bare arrays, tuples, Dictionaries and Hashes and ALL properties.

Instead grab Newtonsoft JSON .NET off the asset store for free, or else install it from the Unity Package Manager (Window → Package Manager).

Also, always be sure to leverage sites like:

https://csharp2json.io

Yes I’m using Newtonsoft JSON .NET usually.

But what bothered me here is that it is not simple JsonUtility not working.

It is literally breaking the system.

Perhaps it has a bug and doesn’t support arrays of tuples.

What happens if you wrap that array of tuples in a type and deserialize that type?

Strange. I’ve tried on Unity 2019.4.40, 2021.3.20 and 2022.2.10 but always get the expected result:

The length you’re getting seems random, makes it seem like some memory corruption.

It is not about the tuple in the end.

Crazy results I got.

Decided to test it on different very fresh project.

unity 2021.3.11

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestComponent : MonoBehaviour
{
    [Serializable]
    public class MyClass
    {
    }
   

    void Start()
    {
            var result0 = JsonUtility.FromJson<MyClass[]>("{}");
            Debug.Log($"Type: {result0.GetType()} IsArray: {result0.GetType().IsArray} Length: {result0.Length}");
           
            var result = JsonUtility.FromJson<int[]>("{}");
            Debug.Log($"Type: {result.GetType()} IsArray: {result.GetType().IsArray} Length: {result.Length}");
           
            var result2 = JsonUtility.FromJson<MyClass[]>("{}");
            Debug.Log($"Type: {result2.GetType()} IsArray: {result2.GetType().IsArray} Length: {result2.Length}");
    }

}

Notice first and last are the same. (result0 and result 2)
But they have different Debug.Log

Type: TestComponent+MyClass[ ] IsArray: True Length: 573562159
UnityEngine.Debug:Log (object)
TestComponent:Start () (at Assets/Scenes/TestComponent.cs:17)

Type: System.Int32[ ] IsArray: True Length: 0
UnityEngine.Debug:Log (object)
TestComponent:Start () (at Assets/Scenes/TestComponent.cs:20)

Type: TestComponent+MyClass[ ] IsArray: True Length: 0
UnityEngine.Debug:Log (object)
TestComponent:Start () (at Assets/Scenes/TestComponent.cs:23)

Only tried in Unity 2022.2 but there it was the middle result that would get the random lengths some of the time. The lengths also changes randomly, making it more plausible that it’s some uninitialized memory.

Note that JsonUtility doesn’t actually support arrays directly, you’d have to wrap them in a class to (de-)serialize them. Maybe it internally errors out at some point, leaving the returned object only half-initialized.

Unity 2021.3.15 is consistently alternating between result0 and result2 having an incorrect value. I passed the code to GPT-4 and it immediately made a comment about JsonUtility not handling arrays of empty objects correctly and suggested the following modification:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestComponent : MonoBehaviour
{
    [Serializable]
    public class MyClass
    {
    }

    T[] SafeJsonArrayFromJson<T>(string json)
    {
        if (json == "{}")
        {
            return new T[0];
        }
        else
        {
            return JsonUtility.FromJson<T[]>(json);
        }
    }

    void Start()
    {
        var result0 = SafeJsonArrayFromJson<MyClass>("{}");
        Debug.Log($"Type: {result0.GetType()} IsArray: {result0.GetType().IsArray} Length: {result0.Length}");

        var result = SafeJsonArrayFromJson<int>("{}");
        Debug.Log($"Type: {result.GetType()} IsArray: {result.GetType().IsArray} Length: {result.Length}");

        var result2 = SafeJsonArrayFromJson<MyClass>("{}");
        Debug.Log($"Type: {result2.GetType()} IsArray: {result2.GetType().IsArray} Length: {result2.Length}");
    }
}
1 Like

You literally made me start using GPT for coding instead of using google and docs and I already got some success with it.

Good for it to pick up the JsonUtility limitation but the solution is nonsense. This would be more appropriate:

public T SafeFromJson<T>(string json)
{
    if (typeof(T).IsArray)
        throw new Exception($"SafeFromJson: JsonUtility does not support arrays directly, wrap them in a class or struct first.");
  
    return JsonUtility.FromJson<T>(json);
}
3 Likes