Serialization of object with MonoBehaviour field

Hi,

I’m trying to reconcile the behavior I’m observing in Unity 5.5 with the API / Manual documentation. I’ve searched high-and-low, and while I’ve found a lot about serializing inherited classes, and understand the well documented and answered limitations, I can’t find anything that addresses this issue.

According to JSON Serialization => Supported Types, “The object you pass in is fed to the standard Unity serializer for processing[…]”, and according to Script Serialization =>Fieldtypes that can be serialized, the standard serializer supports References to GameObjects that derive from UnityEngine.Object, which based on inheritance should include MonoBehaviour.

If I have a class that inherits from a MonoBehaviour, it will serialize properly when passed directly into the serializer, but if that same object is a field of another class, it will only serialize the Instance ID. See a small example of the behavior below. Before I start moving down one of the many alternate paths (e.g., callbacks, data containers, alternate serialization system), am I missing something simple? Misreading the documentation?

A Player Class (Attached to GameObject):

using UnityEngine;

public class Player : MonoBehaviour {
	public float health;
	public string playerName;
}

A Player Data Class with identical fields (Want to Avoid These):

[System.Serializable]
public class PlayerData {
	public float health;
	public string playerName;
}

The Game Data Class with a MonoBehaviour field:

[System.Serializable]
public class GameData {
	public Player player; //Inherits from MonoBehaviour
	public PlayerData playerData;
}

A Data Controller to test the Serialization:

using UnityEngine;

public class DataController : MonoBehaviour {
	void Start () {
		GameData gameData = new GameData();
		Player player = GameObject.Find("Player").GetComponent<Player>();
		PlayerData playerData = new PlayerData();

		player.health = 100f;
		player.playerName = "Player Name";
		gameData.player = player;

		playerData.health = 50f;
		playerData.playerName = "PlayerData Name";
		gameData.playerData = playerData;

		string gameDataJson = JsonUtility.ToJson(gameData);

		Debug.Log(JsonUtility.ToJson(gameData));
		//Logs: {"player":{"instanceID":-3768},"playerData":{"health":50.0,"playerName":"PlayerData Name"}}
		// MonoBehavior not serialized as field of object.

		Debug.Log(JsonUtility.ToJson(player));
		//Logs: {"health":100.0,"playerName":"Player Name"}
		//MonoBehavior can be serialized directly

		gameData.player.playerName = "Updated Player Name";
		GameData restoredGameData = JsonUtility.FromJson<GameData>(gameDataJson);
		Debug.Log(restoredGameData.player.playerName);
		//Logs: "Updated Player Name"
		//Deserialization returns the instance (as expected from instance ID), not the original values
	}
}

Thanks for your help!

Not exactly what you want, but this is the kind of pattern I use. It has many benefits as you are fully aware and in control of what your saving and not. It will also come in handy when updating save games to a new version.

using UnityEngine;
public class Player : MonoBehaviour {

	[System.Serializable]
	public struct Data {
		public float health;
		public string playerName;
	}

	Data save_data;
}