- Home /
Why my Serializable class isn't receiving values from a Generic .json loader?
I'm creating generics for saving and loading process. I really save and load a lot. So, I have decided to make generics. The generic for saving works perfectly, if someone wants it, just take it:
Code:
Save
public static void SaveToJSON<T> (this T data, string path, string fileName)
{
if (!typeof(T).IsClass)
throw new Exception("Only classes can be passed to this method. " + typeof(T).Name + " is not a class.");
if (!typeof(T).IsSerializable)
throw new Exception("The " + typeof(T).Name + "class is not Serializable. Please make it Serializable so the class can be accessed");
string finalPath = path + fileName + ".json";
string newFile = JsonUtility.ToJson(data);
File.WriteAllText(finalPath, newFile);
}
Even though the generics aren't needed for this, they are really useful, and usually I need to break my head down to make the code fit into my codes, and now I don't need to break my head any longer.
The Issue
For some reason the game is not loading properly. It is probably the .json loader, since I'm getting no value back. The .json loader IS converting the .json file correctly, but I don't know why the values aren't being passed to the Serializable class that I want to be loaded. The code is down here, so you can examine it.
Code:
Load
public static void LoadFromJSON<T> (this T data, string path, string fileName)
{
if (!typeof(T).IsClass)
throw new Exception("Only classes can be passed to this method. " + typeof(T).Name + " is not a class.");
if (!typeof(T).IsSerializable)
throw new Exception("The " + typeof(T).Name + "class is not Serializable. Please make it Serializable so the class can be accessed");
string jsonString = string.Empty;
string finalPath = path + fileName + ".json";
jsonString = File.ReadAllText(finalPath);
data = JsonUtility.FromJson<T>(jsonString);
//Debug.Log(jsonString);
}
You can test it, and use it wherever you want, but if you know why it doesn't work let me know, please. I'm open to suggestions on how to make this code even better.
In case this code is working for you, let me know as well, so I'll post another question with another code pieces to know if those are the cause of the issue. They're not relevant here since they're only DictionaryToString() and FromString() converters.
Thanks in advance.
Here is the .json file that I'm trying to load. I think it is needed so you can see clearly what I'm trying to do. Loading this manually, without the Generic works perfectly, but I want the Generic so it can save me some time.
.json File
{
"stats": [
"Health_100",
"MaxHealth_100",
"RegenSpeed_10",
"Stamna_10",
"MaxStamna_10",
"Defense_0",
"DamageMultiplier_1",
"CriticalStikeChance_0",
"Speed_5",
"Level_0",
"CurrentXP_0",
"XPToNextLevel_100"
]
}
The Solution
Okay... A solution found... If anyone reading this just came here to copy/paste, please read @Bunny83 answer. Here is the solved code:
Answer by Bunny83 · Oct 04, 2017 at 06:32 PM
First of all keep in mind that Unity's JsonUtility has the same limitations as the normal serialization system of Unity. So make sure whatever you want to serialize meets those requirements.
Second your Json file looks strange. It just contains a string array. Are that your "stringified" dictionary key-values-pairs?
Finally lets solve your actual problem. Method parameters are generally passed by value and therefore whatever is passed into the method can only be used inside the method unless it's a ref or out parameter. Of course the first parameter of an extension method can not be a ref parameter. Therefore this line:
data = JsonUtility.FromJson<T>(jsonString);
should actually create a new instance of the type "T" and also replace the local variable "data" with the new instance, however you never actually pass that value out of your method. Of course one way would be to use an "out" parameter or simply return the new instance. Though this would make the extension method kind of pointless.
The better solution is to use JsonUtility.FromJsonOverwrite. It allows you to deserialize the JSON and overwrite the values of an existing instance.:
JsonUtility.FromJsonOverwrite(jsonString, data);
Finally All your checks just limit the usage. You can also serialize structs with the JsonUtility. Also it doesn't necessarily need to be marked as serializable as you can also serialize MonoBehaviours with the JsonUtility. Also if you insist throwing your own exception you should throw an appropriate exception. In your case an ArgumentException would be a better fit.
@$$anonymous$$ultipleGameStyles Bunny is right, using JSonUtility.FromJsonOverwrite is a much better option than having an out parameter. Please use his answer ins$$anonymous$$d.
Okay, okay. Thanks, @Bunny83 . Your solution is better. For me, the simple the better. Your solution gives me that. Thanks.
And, answering your question, the .json file is a Dictionary of stats converted into an array of strings.
Answer by MacDx · Oct 04, 2017 at 06:03 PM
When working with extension methods you cannot switch the reference of the calling object as you are doing right now, what you need to do is modify the object contents instead. However since you are working with generics you can't really know the contents of the object so IMO, your best option is to add an extra out parameter of type T and pass the calling object. Like this:
public static void LoadFromJSON<T>(this T data, string path, string fileName, out T dataOut)
{
if (!typeof(T).IsClass)
throw new Exception("Only classes can be passed to this method. " + typeof(T).Name + " is not a class.");
if (!typeof(T).IsSerializable)
throw new Exception("The " + typeof(T).Name +
"class is not Serializable. Please make it Serializable so the class can be accessed");
string jsonString = string.Empty;
string finalPath = path + fileName + ".json";
jsonString = File.ReadAllText(finalPath);
dataOut = JsonUtility.FromJson<T>(jsonString);
Debug.Log(jsonString);
}
And call it like this:
data.LoadFromJSON("Assets/Resources/","dataFile",out data);
Hope this helps!
Edit: Just for extra clarification, this line in your current LoadFromJSON method is what's causing the problem
data = JsonUtility.FromJson<T>(jsonString);
Edit: Bunny's solution is better. Instead of an out parameter use JsonUtility.FromJsonOverwrite instead to modify the object contents.
Thanks, it worked. I didn't knew that the reference couldn't be switched as I was doing. Hopefully this code will help someone else. Thanks again.
Your answer
Follow this Question
Related Questions
LitJson load float 2 Answers
Saving GameObject to file 2 Answers
using json file for waypoints for vehicle in unity project. 0 Answers
Executing code from a text file and writing changes to it 1 Answer