On Unitys Serialization Procotol

Unity's built-in serializer is deliberately very simple and straightforward. This allows for very fast serialization and deserialization times. In this guide, we will examine the rules that Unity's serializer plays by, and try to understand them.

This is relevant when deciding which serializer should be employed, and for trying to ensure that certain types will always be serialized by either Odin or Unity. Or just when trying to use the Unity serializer in general, either with MonoBehaviours, ScriptableObjects, etc or when using the JsonUtility class.

The Serializable Attribute

Unity will only serialize any class or struct that defines the Serializable attribute.

public class MyScriptableObject : ScriptableObject
	// Types without the Serializable attribute will not be serialized by Unity.
	public TypeWithoutSerializable NotSerialized;

	// This type has the Serializable attribute, and thus this field is serialized.
	public TypeWithSerializable Serialized;

public class TypeWithoutSerializable { ... }

public class TypeWithSerializable { ... }

The Type Cannot Be Generic

Unity will not serialize any generic type variant. You can, however, make a concrete definition of a generic class by inheriting from the generic type, and that class and all generic fields (the other rules still apply to these of course) - will be serialized by Unity. Of course, List is a special, hard-coded exception to this rule.

public class MyScriptableObject : ScriptableObject
	// This field is of a generic type and will be not be serialized by Unity.
	public MyGenericClass<int> NonSerialized;

	// Despite the fact that this class inherits from MyGenericClass<int> it does not count
	// as a generic type, and will, therefore, be serialized by Unity.
	// Even the Value field declared in MyGenericClass<T> will also be serialized as an int field.
	public MyIntClass Serialized;

	// Despite being a generic type, Unity makes an exception for the List<T> type.
	public List<int> SerializedList;

// This will not work.
public class MyGenericClass<T>
	public T Value;

// But this is fine.
public class MyIntClass : MyGenericClass<int>

No Polymorphism

Unity will not serialize any polymorphic references, and therefore any abstract, interface or object fields will therefore not be serialized.

public class MyScriptableObject : ScriptableObject
	// Neither the interface nor the abstract type fields will be serialized, because that
	// would require polymorphism.
	public IMyInterface NonSerializedInterface;
	public AbstractType NonSerializedAbstract;

	// This field will be serialized as it does not require polymorphism.
	public InheritedType Serialized;

	// This field will be serialized, but despite us assigning an InheritedType object to it,
	// the field will be serialized as a BaseType field.
	// This means that upon deserialization the field will have a BaseType object assigned
	// to it instead of an InheritedType.
	public BaseType SerializedAsBaseType = new InheritedType();

public interface IMyInterface { ... }
[Serializable] public abstract class AbstractType { ... }
[Serializable] public class BaseType { ... }
[Serializable] public class InheritedType : BaseType { ... }

No DateTime (or other mscorlib stuff)

For whatever reason, and despite following all of the rules laid out here, Unity will not serialize DateTime fields, or any other structs and classes defined in mscorlib.

public class MyScriptableObject : ScriptableObject
	// No DateTime serialization with Unity.
	public DateTime NonSerialized;

Underlying Enum Type

Unity, depending upon version, does not support all underlying enum types. Version 5.6 of Unity introduced support for all primitive types except for long and ulong. Before this version, only byte and int enum types are supported.

public class MyScriptableObject : ScriptableObject
	// All unity verions supports int and byte enum types.
	public MyByteEnum SerializedInAllVersions;

	// Unity 5.6 and up supports short enum types.
	public MyShortEnum SerializedInSomeVersions;

	// Unity does not currently support long enum types.
	public MyLongEnum NotSerialized;

public enum MyByteEnum : byte { ... }
public enum MyShortEnum : short { ... }
public enum MyLongEnum : long { ... }

As one might expect, these rules are also extended to array element types and the generic arguments of List.