Handling inheritance with JSONUtility.ToJSON

I’m trying to use the new JSONUtility to serialize an object that contains an object that is the descendant of a base class. Here’s an example:

[Serializable]
public class Class1
{
    public int class1Field = 1;
    public Class2 attributes = new Class3();
}
[Serializable]
public class Class3 : Class2
{
    public int class3Field = 3;
}
[Serializable]
public class Class2
{
    public int class2Field = 2;
}

If I then do:

Class1 myClass = new Class1();
Debug.Log(JsonUtility.ToJson(myClass));

The output is:

{“class1Field”:1,“attributes”:{“class2Field”:2}}.

As you can see, nothing from Class3 is being serialized. Interestingly if I do:

Debug.Log(JsonUtility.ToJson(myClass.attributes));

The output is :

{“class2Field”:2,“class3Field”:3}

which is what I expect. I know that I can do a pretty simple workaround using string.Format, but is there a more elegant solution to this issue? Am I perhaps not using the JSONUtility correctly? Or is this simply not supported by the JSONUtility at this time (Unity 5.3.2)?

To be clear, my desired output is:

{“class1Field”:1,“attributes”:{“class2Field”:2,“class3Field”:3}}

I still use Unity 5.2.1f1 and i don’t have the JSONUtility class ^^.

However i guess that they did the same as they did with the serialization system in Unity. They serialize the fields based on the field type, not on the actual runtime type. If you want to support serialization and deserialization you have two choices:

  • either you have to serialize the actual runtime type as well so when deserializing the data the deserializer can recreate the correct type
  • or don’t support inheritance and only serialize the pure data. When deserializing the data you usually have to specify the type of the top object. From there on the deserializer can simply use the field types of the given class to determine the object types it needs to create. This of course doesn’t support inheritance since if the field type has a baseclass type there’s no way how the deserializer can determine which derived type it has to instantiate. This information is simply missing in the serialized data.

Your “desired output” also misses the information which class type the attributes sub object has. Of course you could create a “smart” deserializer which looks at the field type, enumerates it’s fields and matches the found fields with the given serialized data. If there are missing fields it could search for a subclass of the given field type and try to find one that matches the serialized fields. However that’s quite complicated and isn’t a reliable approach. If a subclass doesn’t add new fields, it would still create a baseclass instance. It’s also possible that two child classes have the same fields so again it can’t reliable decide which class to use.