How to serialize nested objects?

Hello. I am trying to build a safe game functionality. I am able to serialize a serializable class I wrote, but I am not able to figure out how to serialize that class, it the class has child object.

When I run the save method, this line: info.AddValue(“Coins”, saveObject.playerInventory.coins);
produces a null reference exception (saveObject.playerInventory is null). If I exclude the playerInventory object from the save, it works fine. I have found examples of how to do this when it is just one object, but I have been unable to find any examples of nested objects. Can this be done? Am I going about this the wrong way?

Here are my classes:

[CreateAssetMenu(fileName = "New Save Object", menuName = "Save Object")]
[System.Serializable]
public class SaveObject : ScriptableObject
{
  
    public Inventory playerInventory;
    public string sceneName;

}
[CreateAssetMenu(fileName = "New Inventory", menuName = "Inventory/Inventory")]
[System.Serializable]
public class Inventory : ScriptableObject
{
    public Item currentItem;
    public List<Item> Items = new List<Item>();
    public int coins;
    public FloatValue PlayerHealth;
    public FloatValue PlayerMana;
    public FloatValue PlayerStrength;
    public FloatValue PlayerXP;
    public IntegerValue PlayerLevel;
    public FloatValue PlayerGold;
    public PlayerPosition PlayerPostion;

    public void RemoveInventoryItem(Item item)
    {
        Items.Remove(item);
    }

    public void addCoins(int coin)
    {
        coins = coins + coin;
    }
    public void removeCoins(int coin)
    {
        coins = coins - coin;
    }
    public void AddHealth(int points)
    {
        if (PlayerHealth.RuntimeValue < PlayerHealth.MaxValue)
        {
            PlayerHealth.RuntimeValue += points;
        }
        else
        {
            PlayerHealth.RuntimeValue = PlayerHealth.MaxValue;
        }

    }
    public void AddStamina(int points)
    {
        if (PlayerStrength.RuntimeValue < PlayerStrength.MaxValue)
        {
            PlayerStrength.RuntimeValue += points;
        }
        else
        {
            PlayerStrength.RuntimeValue = PlayerStrength.MaxValue;
        }

    }
    public void AddMana(int points)
    {
        if (PlayerStrength.RuntimeValue < PlayerStrength.MaxValue)
        {
            PlayerStrength.RuntimeValue += points;
        }
        else
        {
            PlayerStrength.RuntimeValue = PlayerStrength.MaxValue;
        }

    }

}

And my serialize object:

public class SavePlayer : Singleton<SavePlayer>
{
    [SerializeField] public SaveObject playerSaveObject;
    public void SaveGameState(string saveName, string sceneName)
    {
        string SaveFolder = Application.persistentDataPath + "/saves/";
        string SavePath = SaveFolder + saveName + ".save";   
        BinaryFormatter formatter = GetBinaryFormatter();
        if (!Directory.Exists(SaveFolder))
        {
            Directory.CreateDirectory(SaveFolder);
        }
        FileStream file = File.Create(SavePath);
        playerSaveObject.sceneName = sceneName;
        formatter.Serialize(file, playerSaveObject);       
        file.Close();
    }
    public void LoadGameState(string SavePath)
    {
        if (!File.Exists(SavePath))
        {
            Debug.LogErrorFormat("File does not exist {0}", SavePath);
        }
        BinaryFormatter formatter = GetBinaryFormatter();
        FileStream file = File.Open(SavePath, FileMode.Open);
        try
        {
            object save = formatter.Deserialize(file);
            SaveObject saveObj = (SaveObject)save;          
            playerSaveObject.sceneName = saveObj.sceneName;           

        }
        catch (Exception ex)
        {
            Debug.LogErrorFormat("Failed to load file at {0}", ex);
        }
        finally
        {
            file.Close();
        }
    }
    public BinaryFormatter GetBinaryFormatter()
    {
        BinaryFormatter formatter = new BinaryFormatter();
        SurrogateSelector surrogateSelector = new SurrogateSelector();

        SavePlayerObjectSerializationSurrogate SavePlayerObjectSerializationSurrogate = new SavePlayerObjectSerializationSurrogate();
   
        surrogateSelector.AddSurrogate(typeof(SaveObject), new StreamingContext(StreamingContextStates.All), SavePlayerObjectSerializationSurrogate);
      
        formatter.SurrogateSelector = surrogateSelector;
        return formatter;
    }

}

public class SavePlayerObjectSerializationSurrogate : ISerializationSurrogate
{
    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        SaveObject saveObject = (SaveObject)obj;

        info.AddValue("scene_name", saveObject.sceneName);
        info.AddValue("coins", saveObject.playerInventory.coins);  

    }
    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        SaveObject saveObject = (SaveObject)obj;          
        saveObject.sceneName = (string)info.GetValue("scene_name", typeof(string));
        saveObject.playerInventory.coins = (int)info.GetValue("coins", typeof(int)); 

        return obj;
    }
}

It isn’t so important WHAT you’re trying to do, but it’s important that the variables you are doing it with are NOT null at the moment you are doing it. :slight_smile:

Generally for serializing objects, everything in the entire hierarchy needs to be decorated with System.Serializable attribute.

It’s not really possible for me to gaze at the above code and imagine how you are setting this all up, but if your program design requires a valid playerInventory in order to do the save, well, you gotta get the inventory object in there, however and whatever that means in your program.

Remember, the answer is always the same… ALWAYS. It is the single most common error ever. Don’t waste your life on this problem. Instead, learn how to fix it fast… it’s EASY!!

Some notes on how to fix a NullReferenceException error in Unity3D

  • also known as: Unassigned Reference Exception
  • also known as: Missing Reference Exception

http://plbm.com/?p=221

The basic steps outlined above are:

  • Identify what is null
  • Identify why it is null
  • Fix that.

Expect to see this error a LOT. It’s easily the most common thing to do when working. Learn how to fix it rapidly. It’s easy. See the above link for more tips.

This is the kind of mindset and thinking process you need to bring to this problem:

Step by step, break it down, find the problem.

Thanks for the response, but I am not sure it’s aligned to what my issue was. There were no null exceptions raised. The issue was that the formatters were converting the custom object from an object to a bool. Maybe my question was not clear? But what I needed help with was desieralizing a nested object, where the nested object would deserialize as a bool, instead of what it was. But, I ended up serializing using json instead, and that seemed an easier route.

Oh, I just noticed the above… that’s not good! Check this:

I suggest trying JSON or something more modern and safe.

Further I recommend the Newtonsoft JSON .NET package, available free on the Unity asset store.