How to catch Unity deserialising (file loading) blowing up?

edited in General
Hi guys

I know what the problem is but I can't figure out how to catch the exception in the code...

I'm using this code to save/load data:

if (File.Exists(Application.persistentDataPath + "/gamesave.save"))
		{
			BinaryFormatter bf = new BinaryFormatter();
			FileStream file = File.Open(Application.persistentDataPath + "/gamesave.save", FileMode.Open);
			GameData save = (GameData)bf.Deserialize(file);
			file.Close();
			gameData = save;
		}


It's been working fine until I recently started adding/removing arrays to the GameData type. Now, adding other variables over time seemed to be fine, but somehow arrays break it. I understand why it's breaking - it breaks because it tries to load the old file, and when it tries to deserialise the data into the GameData class, there are mismatches, and so it breaks.

While I can manually tell the game to reset the save file no problem, If I do that outside of unity when I deploy, I can only either ALWAYS reset the save file, rendering saving useless, or never reset the file, which throws the error always.

How do I ask the script to catch the exception when the deserialisation fails, so I can tell it to start a new file?

Thanks guys!

Comments

  • Maybe You could make a separate save which is just a version number and every time you make a change to the save format, change the version number and check that before deserializing the main file? Bit of a long workaround but will work.
    Thanked by 1Tuism
  • vintar said:
    Maybe You could make a separate save which is just a version number and every time you make a change to the save format, change the version number and check that before deserializing the main file? Bit of a long workaround but will work.
    Man that makes sense, short of something that'll actually catch the exception I think this will work :) Yay thanks! :)
  • You could also just wrap this in a try catch and do something based on that.
    Thanked by 2vince mattbenic
  • This has always been a pain for me. You can always add stuff, but once something is in a serialized class, it's a pain to get out. So if anyone has some tips for adding/removing fields from save files I'd also appreciate it...

    I use XML Serialization + compression instead of a binary format, the advantage is that you can save to plaintext if you want to inspect it manually, and you can mark deprecated fields with ignore tags so they at least don't get written to disk. It doesn't scale as well for larger save files though.
    Thanked by 1NickCuthbert
  • On just detecting the error, try/catch around your load code as @francoisvn suggested and handle the fail case in the catch:
    SaveData saveData;
    try
    {
        saveData = YourDeserializationMethod();
    }
    catch (Exception ex) // Usually best practice to handle specific exception types here, but being general
    {
        YourErrorReportingMethod(ex);
        saveData = YourDefaultSaveData;
    } 
    	
    DoStuffWithSaveData(saveData);


    Personally I'm a fan of json for the ease of readability and direct mapping to objects and have used json.net for ages. You can optionally run a second pass by zipping and password protecting your savedata towards the end of development.

    On data versioning, it's worth having a migration plan in place for old data rather than just starting with fresh data for failed loads. Consider having a save metadata format that will not change, something like just the file version and a datetime, and possibly some kind of checksum. This goes in a separate file to your actual save data. You load it first, compare it to your current save version (ie the version of the save/load code that is running) and act accordingly:
    -if it's the current version, great. Load up the save file and carry on
    -if it's a newer version, request the user update their app/game/whatever
    -if it's an older version, load it and upgrade it, then save it out again and carry on as normal

    How that "load it and upgrade it" works can vary.
    -If you've just added some new fields since that version, populate them with reasonable default data.
    -If you've renamed a field keep it around with an [obsolete] attribute (so you don't accidentally use it in code) and copy that data over to the new version. Depending on your serialization library, it may have support for alternate serialized names as well.
    -If you have deleted or changed the types of fields, it may be worth keeping that old version of your savedata class around (class SaveData_v1_2_3), loading the data using that class (json.net generic deserialization for example) and then doing a migration (saveData = new SaveData(oldSaveData)).
Sign In or Register to comment.