using System;
using UnityEngine;
public static class JsonHelper
{
public static T[] FromJson<T>(string json)
{
Wrapper<T> wrapper = JsonUtility.FromJson<Wrapper<T>>(json);
return wrapper.Items;
}
public static string ToJson<T>(T[] array)
{
Wrapper<T> wrapper = new Wrapper<T>();
wrapper.Items = array;
return JsonUtility.ToJson(wrapper);
}
public static string ToJson<T>(T[] array, bool prettyPrint)
{
Wrapper<T> wrapper = new Wrapper<T>();
wrapper.Items = array;
return JsonUtility.ToJson(wrapper, prettyPrint);
}
[Serializable]
private class Wrapper<T>
{
public T[] Items;
}
}
Which looks ok to me, I didnât find any errors or anything, but when I load a Json file and try to get an array, it doesnât show an error until I try to acces the array, so in this example the error will be caused:
//a custom class I made
scenario[] scenarios;
//uses the JsonHelper to get the scenarios array
scenarios = JsonHelper.FromJson<scenario>(scenariosJson.text);
//this line causes an error
string text = scenarios[0];
the json file:
{
"scenarios":[
{"id":"0",
"text": "I am {character.age}, {(characetr.age < 50 ? \"I still got some time\" : \"Guess I am getting olf\" )}",
"popupText": "You are {(characetr.age < 50 ? \"Young\" : \"Old\" )}",
"conditions": ""},
{"id":"1",
"text": "I am {character.firstName} {character.lastName}",
"popupText": "You are {character.firstName} {character.lastName}",
"conditions": ""}
]
}
the error message:
NullReferenceException: Object reference not set to an instance of an object
GameManager.addDay () (at Assets/scripts/GameManager.cs:27)
UnityEngine.Events.InvokableCall.Invoke () (at <4a31731933e0419ca5a995305014ad37>:0)
UnityEngine.Events.UnityEvent.Invoke () (at <4a31731933e0419ca5a995305014ad37>:0)
UnityEngine.UI.Button.Press () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:70)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:114)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:57)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:501)
Iâm fairly certain Unityâs bare bones JsonUtility will fail to properly serialise/deserialise generics. You will need to use a more robust serialiser.
Yes, thatâs what I would have suggested. While the idea of the wrapper was meant as a workaround to serialize an array and to deserialize that array again, since your original json did actually had a different key name, you could have simply created the proper class that matches your json.
Specifically
[System.Serializable]
public class ScenarioRoot
{
public scenario[] scenarios;
}
So instead of loading / saving your scenarios array with the âJsonHelperâ, you could simply use Unityâs JsonUtility on the ScenarioRoot object.
ps: class / type names should always start with an upper case letter. Calling your class âscenarioâ is really strange and can easily lead to confusion down the line.
The problem with creaing a class to represent the json is I will use alot of json files, as the game is text based and I need to store all the text data in files, and most of the data will be arrays, so it means I have to make 2 classes(item and root) which means I will end up with many classes, which I donât want, and this is where Generics come into place(Reducing the classes by half).
And thanks for noting I should start my classes names with upper case letters, I will keep it in mind.
If you donât want to create classes to represent the Json, you should look at Newtonsoft Json instead.
The dictionary-access you can use through that will make it a lot easier. https://www.newtonsoft.com/json
Donât complicate your life and just use JsonUtility. It has its limitations that you have to adjust to (change what it donât or canât use properly, change Array to List). Everything is described in detail below. Three methods to remember, why wrap it in some wrapper classes like this Helper?
The only rule to remember: Only plain classes and structures are supported; classes derived from UnityEngine.Object (such as MonoBehaviour or ScriptableObject) are not.
In short, game objects are supposed to wrap data from JSON, not the other way around.
JsonUtility has been getting hate from everyone for years for a reason.
Hell, even Unityâs own developers use Newtonsoft instead (which is why itâs a dependency in among others: Version Control, Unity Collections, ECS, etc.)
JsonUtility is often the best Json serializer to use in Unity. Itâs built-in, fast and simple to use when youâre already familiar with Unityâs serialization rules. Though itâs definitely not for every use case, especially when you need to read or write Json thatâs used outside of Unity, you need the flexibility of a full Json library.
Well, those âreasonsâ are most of the time hyped way too much. The JsonUtility is just a text / json based extension of Unityâs own serialization system and therefore has the same limitations. In comparison to almost all other json solutions, Unityâs JsonUtility is one of the fastest, probably because of its limitations.
Depending on the usecase there are many different solutions available and JsonUtility, as long as you can live with / work around its limitations, is just one of them.
Well, itâs not clear how your project actually scales and what kind of information you may store in the json. However you usually would have all necessary data in a single json structure. Using generics does not reduce the number of classes at all, it just hides the generation of those from you. The actual generic class is JIT compiled at runtime when a type is bound to a generic type parameter. So when you use for example the generic List class with string, int, Vector3 and GameObject, you will actually have 4 distinct classes that the JIT compiler (or AoT compiler for IL2CPP) would generate.
If you just want to load json without dealing with object mapping at all, you can use my SimpleJSON parser. Itâs a single cs file and allows parsing and generating json data and also provides easy access to it. Internally it just parses the data into JSONObjects (internally a Dictionary), JSONArrays (internally a List), JSONStrings, JSONNumber (just a double value) and JSONBool. Itâs very extensible to customize or extend the built-in functionality. Iâve also made a Unity specific extension file that just needs to sit next to the main file. It gives implicit conversion support for some Unity types like Vector2/3/4, Color, Quaternion and some other types. Though as I said, you could add your own additions to convert to / from your own types.
ps: Iâm wondering: Youâre using what looks like string interpolation in the texts you store in your json file. However you canât really interpret those when the string is loaded dynamically at runtime. Do you have your own string interpolator that works at runtime?
Yes. You need to âlive withâ or âwork aroundâ its limitations.
I was responding to:
Working around limitations IS complicating your life. Which is why everyone switches to another library as soon as they need to do more than very basic serialization/deserialization.
No, thatâs only the case when you actually need a workaround Especially when there are already solutions for certain cases. In most cases itâs quite trivial to adapt to the slight change necessary to make it work natively with JsonUtility. You know, for a small Unity project, all your compiled game code is probably 10% of the size of the Newtonsoft parser Donât get me wrong, the Json.NET library is really a great library, but even there you bump into limitations that require workarounds. Vector3 or other Unity types. You may need some converters or object generators that you can register to Json.NET and it handles everything for you. But you still have to do those things because even with Json.NET there are cases that need workarounds or adjustments. Maybe just another attribute, maybe some sort of ContractResolver / JsonConverter magic. I just react a bit allergic against those who praise one solution and condemn another âjust becauseâ.
As with any piece of software or library, you have to know its limitations and decide what fits your usecase and what amount of adjustments you can and want to tolerate.