Hello dear community,
I found a weird bug in my code and tried to trace it down. It turned out that the FromJson function is calling the default contructor although the documentation says “the constructor for the object is not executed during deserialization”. (Unity - Scripting API: JsonUtility.FromJson)
I have a DataManager for saving/loading data with this function:
public static InternalDataContainer LoadInternalData()
{
InternalDataContainer internalDataContainer = gameStateFileDataHandler.Load<InternalDataContainer>(internalDataFileName);
internalDataContainer ??= new InternalDataContainer();
return internalDataContainer;
}
The load function is simply doing the following:
public override T Load<T>(string key)
{
string data = readFile(key);
if (data == "")
return default(T);
T loadedObj = JsonUtility.FromJson<T>(data);
return loadedObj;
}
And the InternalDataContainer class is defined as follows:
[Serializable]
public class InternalDataContainer
{
/// Lots of fields...
[field:SerializeField]
public SerializableDateTime TimeStamp { get; set; }
public InternalDataContainer()
{
// Assignment of default values for all those fields
TimeStamp = new SerializableDateTime(DateTime.Now);
}
}
Now, it all worked, except loading the TimeStamp from the .json file. Although the time was correct in the file, after loading the .json file, the TimeStamp was always DateTime.Now.
I found the Bug, it was inside the class SerializableDateTime:
[Serializable]
public class SerializableDateTime
{
[SerializeField]
private long ticks;
private bool initialized;
private DateTime dateTime;
public DateTime DateTime
{
get
{
if (!initialized)
{
dateTime = new DateTime(ticks);
initialized = true;
}
return dateTime;
}
}
public SerializableDateTime(DateTime dateTime)
{
ticks = dateTime.Ticks;
// Here is the Bug: I had to remove those two lines (or at least the last one)
//this.dateTime = dateTime;
//initialized = true;
}
}
The reason was: When having the default assignment (with DateTime.Now) in the class constructor, the SerializableDateTime constructor was called before JsonUtility.FromJson overrides the members. (i.e. before OnAfterDeserialize is called)
=> Then the datetime is assigned a wrong value and it will never be updated to the afterwards correctly deserialized ticks, because initialized is already set to true.
It took me a couple of hours to trace it down and I am just very confused why the default constructor is executed when calling FromJson because the documentation explicitly states that is is not called.
Am I doing a mistake here or is this info in the docs simply wrong?
Greetings