Hello! So Im having next object and I want to have initialized DateTime field after deserialization with JsonUtility.FromJson();
[Serializable]
public class LastActivatedDailyRewardsData : ISerializationCallbackReceiver
{
public int lastActivatedRewardIndex;
public string activationTimeString;
public DateTime activationTime {get; private set;}
public LastActivatedDailyRewardsData(int lastActivatedRewardIndex, DateTime activationTime) {
this.lastActivatedRewardIndex = lastActivatedRewardIndex;
this.activationTime = activationTime;
activationTimeString = TimeToString(activationTime);
}
string TimeToString(DateTime activationTime) {
return activationTime.ToString("u");
}
public void OnBeforeSerialize(){}
public void OnAfterDeserialize()
{
activationTime = DateTime.Parse(activationTimeString).ToUniversalTime();
}
It works ok, but for some reason this âOnAfterDeserializeâ method is called with game start even if I didnt try to deserialize anything. Log message in this method shows that int and string fields of this mistery object are 0 and ââ so it fails with format exception.
So is it ok for this interface to call some mistery empty object or Im using this interface in wrong way?
Assets are reserialised more often than you would expect. Particularly during domain reloads, which happens when entering play mode. The behaviour here is expected.
That said, I would not be using the interface if you donât intend to use both callbacks. The intent is to convert data that canât be serialised into some that can in OnBeforeSerialize, and then convert said serializable data back again in OnAfterDeserialize. Doing only one of the two defeats its purpose.
If you only want it to happen once, then have your property lazily initialize an underlying value when its accessed for the first time. I often use this pattern as ISerializationCallbackReceiver can get fairly heavy in complicated inspectors as assets are reserialised every repaint in any inspector with IMGUI.
Whenever you have used âLastActivatedDailyRewardsDataâ somewhere in a serialized runtime class, it may be serialized / deserialized, even when itâs not part of an explictily serialized object. The reason is that the editor will also serialize / deserialize private variables which are not marked with SerializeField during domain reloads. You can prevent this by marking private variables with NonSerialized. This will exclude this variable from serialization completely. Of course that means that during hot-reloading the instance would be lost.
ps: Do you need the activation time as a string because it may be read by some external API? If you just want to serialize it so it can be loaded back, itâs more compact and faster to store the Ticks of the DateTime as a single long value. Ticks are in 100 nano-second intervals since midnight of 0001.01.01. It may need to be combined with ToUniversalTime if itâs important to be universal, otherwise it would be local time. Though this all depends on your exact usecase.
Whenever you have used âLastActivatedDailyRewardsData â somewhere in a serialized runtime class, it
By this you mean âused LastActivatedDailyRewardsData in another class marked as [Serializable] even not as property to serialize to?â If yes - my answer is no, Im using it only to handy work with values stored in json format in properties. So its only mentioned In âpoperty managerâ and some âmono behaviourâ classes. In my previous researches I found some info about objects marked as [Serializable] actually having ISerializationCallbackReceiver interface. So is it possible my problem is caused of using [Serializable] and ISerializationCallbackReceiver at the same class?
ps-answer: yes, Im using date as string only to store it in properties for local usage, thank you I will start to store date as âlongâ of ticks from this day)
Like the first method, though DateTime is a struct so it wonât ever be null. You wouldnât need to check it isnât something like DateTime.MinValue, or potentially use a nullable value type, ergo DateTime?.
What do you mean by âmentionedâ in mono behaviour classes? MonoBehaviours are always serialized and it includes all fields. So are there any field of that type, somewhere? Since you put a Debug.Log in your callback, what does the callstack look like? It should give some hints where itâs actually deserialized.
Unity does not call any of those callbacks on random classes. Unity itself only serializes MonoBehaviour / ScriptableObject classes (and any data that is nested in those) or when you use the JsonUtility on a Serializable class. One of those cases must be the reason.
Yes, there is private field of LastActivatedDailyRewardsData type in monobehaviour script (intialized in âAwakeâ, used and updated by new objects in some methods)
But the problem is Im having double call of âOnAfterDeserializeâ method just after I pressed âPlayâ button in editor. But only one deserialization is expected before you start make some changes in the game. And if I comment [Serializable] tag in LastActivatedDailyRewardsData class, leaving it only with ISerializationCallbackReceiver interface I stop recieving this unexpected double calls
Log callback for unexpected call looks pretty empty:
Have you tried putting System.NonSerialized on that private field? This would prevent Unity from serializing (and I guess it also should prevent deserializing) this field at all. This behaviour is mentioned here.
Unity restores them to their original, pre-serialization values:
Unity restores all variables - including private variables - that fulfill the requirements for serialization, even if a variable has no [SerializeField] attribute. Sometimes, you need to prevent Unity from restoring private variables, for example, if you want a reference to be null after reloading from scripts. In this case, use the [field: NonSerialized] attribute.
Unity never restores static variables, so donât use static variables for states that you need to keep after Unity reloads a script because the reloading process will discard them.
Yes, you are right, I added [NonSerialized] to my field in monobehaviour and the unexpected deserialization method calls are gone, while the LastActivatedDailyRewardsData class still has [Serializable] and ISerializationCallbackReceiver at the same time.
Thanks for the repeated and patient explanations, didnât get the idea from the first time)