Well, the JsonUtility is rather new when looking at the Unity history. (the talk you’ve linked was from Dec 2016 so not even a year ago).
The JsonUtility basically has the same [rules as the normal Unity serialization system][1] though with some restrictions.
As you said it does not support references to other UnityEngine.Object derived types as there is no general way how to get such references back during deserialization. You have to deserialize every UnityEngine.Object derived object on it’s own.
Instance IDs are only unique for one “session”. So deserializing a ScriptableObject at runtime will give that instance a new instance ID. The Instance ID actually is not serialized. It’s mainly used by the editor
I haven’t really used / tested the JsonUtility well to the bone. Though i hope that it also makes use of the [ISerializationCallbackReceiver interface][2]. It would allow to circumvent most limitations the serialization system has.
I may setup a test case and report back to give a certain answer on that matter.
edit
So yes, the ISerializationCallbackReceiver interface does work when serializing / deserializing with the JsonUtility. So you can easily ship your own reference system if really necessary. Though the objects that you want to reference need to be managed in a static way. You can use a simple object tracker [like this one][3]. The only requirement is that each object you want to track has a unique ID string. You could simply use a GUID.
A tracked object would register “itself” to the tracker in “OnAfterDeserialize”. At this point the object has it’s ID loaded from the file (given your class has a string field for the ID). Likewise any class that holds references to other tracked objects will “request” an object reference based on a given ID. The Tracker allows “late binding”. So if you deserialize an object that has a reference to an object that hasn’t been loaded yet, it will initialize it once the object is registrated.
For a class to register itself to the tracker you would use:
ObjectTracker.Instance.AddObject(objID, this);
where “objID” would be the unique ID of this object.
To “deserialize” a reference you would use this pattern:
ObjectTracker.Instance.GetObject(refID, o=>yourMemberVariable = o);
Note that yourMemberVariable = o
is the code that might be executed immediately or delayed. So make sure the code in that delegate is able to set the reference to the correct variable.
A very quick example:
public interface ITrackableObject
{
string GetID();
}
public class TestObject : ScriptableObject, ITrackableObject, ISerializationCallbackReceiver
{
public string myID;
[System.NonSerialized]
public List<ITrackableObject> someObjects;
[SerializeField]
private List<string> m_TrackedIDs = new List<string>();
public string GetID()
{
return myID;
}
public void OnAfterDeserialize()
{
ObjectTracker.Instance.AddObject(myID, this);
someObjects = new List<ITrackableObject>();
for(int i = 0; i < m_TrackedIDs.Count; i++)
{
int index = someObjects.Count;
someObjects.Add(null); // add a null item to the list to prepare the "variable".
ObjectTracker.Instance.GetObject(m_TrackedIDs*, o=> someObjects[index] = (ITrackableObject)o);*
}
}
public void OnBeforeSerialize()
{
m_TrackedIDs.Clear();
foreach(var o in someObjects)
{
m_TrackedIDs.Add(o.GetID());
}
}
}
The “ITrackableObject” interface is not necessary but it provides a generic way for other classes to obtain the IDs of other objects they might reference.
Note: instead of adding a “null” item to the list and then using the index of that item in the delegate you could simply use:
ObjectTracker.Instance.GetObject(m_TrackedIDs*, o=> someObjects.Add( (ITrackableObject)o));
However this will not preserve the order of the objects in the list as the delegates might be called in a different order.
_[1]: https://docs.unity3d.com/Manual/script-Serialization.html*_
_[2]: https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html*_
_[3]: https://pastebin.com/T1g9HWpr*_