6

I have been messing around with saving and loading in Unity in which I save a serialized class to a file. I have a Serializable class :

[Serializable]
class Save
{
    public List<int> ID = new List<int>();
    public List<int> Amounts = new List<int>();
}

and save it to a file A-OK. I can load it with no errors but if I wanted to add later :

[Serializable]
class Save
{
    public List<int> ID = new List<int>();
    public List<int> Amounts = new List<int>();
    public int extra = 0;
}

and I run my scripts it gives me a deserialization error which I completely understand as when I cast the deserialized file to my new class 'Saved' the new variable I added doesn't exist and it gives me the error.

I found this error when I was fixing an Asset in the store and I know one fix can be just change the filename so a new file is created but I don't just want to wipe the contents of what was saved before.

So my question is, if I wanted to add more variables to my serialized class how would I catch and adjust to old versions of it if people were to update the asset?

Thanks!

Programmer
  • 121,791
  • 22
  • 236
  • 328
JoeyL
  • 1,295
  • 7
  • 28
  • 50
  • 1
    This is a known problem and I believe this has been answered many times on this site. I just can't find the duplicate question on this site but you can solve this by using Json and PlayerPrefs. You can always add or remove items without any error. [Here](http://stackoverflow.com/a/36244111/3785314) is an example using Json.. – Programmer Oct 17 '16 at 04:37
  • 1
    I agree with @Programmer JSON is definitely better than serialization. – Bizhan Oct 17 '16 at 11:40
  • @Programmer Ok cool, So based on what I linked above I am using the ONE DATA way of doing it. When I do JsonUtility.ToJson() I get a string as a return. Do I then serialize that string (and put in a file) or what would you use to have that information in that string be saved so if the user turns off the game and back on I still have that info saved somewhere? If I end up adding more to my class Save would I just do JsonUtility.FromJsonOverwrite()? Sorry if I am asking too many Q's but I feel I am very close to solving my problem which in all honestly seems very simple. – JoeyL Oct 17 '16 at 22:25
  • Check my answer. Maybe it made things clear. – Programmer Oct 18 '16 at 00:20
  • Never trust the serialization system of Unity3D. It's buggy! I would rather write (de)serializer myself just as what Programmer suggested. – zwcloud Nov 20 '16 at 12:54

1 Answers1

23

This problem is known when using C# serializer. Convert the data to Json with JsonUtility then save it with the PlayerPrefs. When loading, load with the PlayerPrefs then convert the json back to class with JsonUtility.

Example class to Save:

[Serializable]
public class Save
{
    public List<int> ID = new List<int>();
    public List<int> Amounts = new List<int>();
    public int extra = 0;
    public float highScore = 0;
}

Save Data:

void Save()
{
    Save saveData = new Save();
    saveData.extra = 99;
    saveData.highScore = 40;

    //Convert to Json
    string jsonData = JsonUtility.ToJson(saveData);
    //Save Json string
    PlayerPrefs.SetString("MySettings", jsonData);
    PlayerPrefs.Save();
}

Load Data:

void Load()
{
    //Load saved Json
    string jsonData = PlayerPrefs.GetString("MySettings");
    //Convert to Class
    Save loadedData = JsonUtility.FromJson<Save>(jsonData);

    //Display saved data
    Debug.Log("Extra: " + loadedData.extra);
    Debug.Log("High Score: " + loadedData.highScore);

    for (int i = 0; i < loadedData.ID.Count; i++)
    {
        Debug.Log("ID: " + loadedData.ID[i]);
    }
    for (int i = 0; i < loadedData.Amounts.Count; i++)
    {
        Debug.Log("Amounts: " + loadedData.Amounts[i]);
    }
}

Difference between JsonUtility.FromJson and JsonUtility.FromJsonOverwrite:

A.JsonUtility.FromJson creates new Object from Json and returns it. It allocates memory.

string jsonData = PlayerPrefs.GetString("MySettings");
//Convert to Class. FromJson creates new Save instance
Save loadedData = JsonUtility.FromJson<Save>(jsonData);

B.JsonUtility.FromJsonOverwrite does not create new Object. It has nothing to do with adding more datatype to your class. It just overwrite the data that is passed in it. It's good for memory conservation and less GC. The only time it will allocate memory if when you have fields such as array, string and List.

Example of where JsonUtility.FromJsonOverwrite should be used is when performing constant data transfer with Json. It will improve performance.

//Create Save instance **once** in the Start or Awake function
Save loadedData = null;
void Start()
{
    //loadedData instance is created once
    loadedData = new Save();
}

void Load()
{
    string jsonData = PlayerPrefs.GetString("MySettings");
    //Convert to Class but don't create new Save Object. Re-use loadedData and overwrite old data in it
    JsonUtility.FromJsonOverwrite(jsonData, loadedData);
    Debug.Log("High Score: " + loadedData.highScore);
}
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Thank you once again Programmer for yet another detailed explanation that made this as easy to understand as learning my abc's. – JoeyL Oct 18 '16 at 00:38
  • 1
    You are welcome. You can encrypt the json data before saving it to make it hard for players to tamper the saved data. Just Google "C# string encryption". You shouldn't worry about that but some do. Happy coding! – Programmer Oct 18 '16 at 00:42
  • I was just about to ask about the safety of saving the json data through player prefs. You're a god damn mind reading genius Programmer. – JoeyL Oct 18 '16 at 00:44
  • 1
    Lol not safe at all. But encryption will make it safer. See [here](http://www.csharpdeveloping.net/Snippet/how_to_encrypt_decrypt_using_asymmetric_algorithm_rsa) for simple encrypt and decrypt function. Feel free to ask new question if you are not able to get it working with that. Although the information on that link is very easy to understand with the amount of example they provided. – Programmer Oct 18 '16 at 00:48
  • This is for you Programmer : https://s-media-cache-ak0.pinimg.com/originals/3b/ba/18/3bba18ea0b75ca4df2bd69d01c5f785d.jpg. I failed at shortening it :( – JoeyL Oct 18 '16 at 01:05
  • 1
    another million!! – Fattie Nov 19 '16 at 16:07
  • i wish i could give a million bounty! – Fattie Nov 23 '16 at 18:08
  • I just freaked out because I saw +100 then I looked and saw where it came from. Thanks once again. – Programmer Nov 23 '16 at 18:23
  • 1
    another million for `FromJsonOverwrite` – Umair M Dec 10 '16 at 13:09