Serialize Anything

Odin includes a powerful serialization system that lets you use types in the inspector that you normally wouldn't be able to. It adds support for polymorphism, references (even cyclic references), and proper null values - all of which are also supported in Odin's inspector itself. It is made to be very extendable and configurable, giving you a lot of control over how and why something is serialized.

Odin's serialization is currently supported on all platforms, save Universal Windows Platform without IL2CPP.

How to use the serialization

While Odin by default injects itself into the inspector for every type that doesn't already have a custom editor defined, the serialization system is more conservative than that. You always have to explicitly decide to make use of Odin's serialization system.

Using it is very simple, though: you merely have to inherit from one of our special Serialized classes, such as SerializedMonoBehaviour or SerializedScriptableObject; all commonly inherited Unity types are supported in this way.


public class YourMonoBehaviour : SerializedMonoBehaviour
{
    [SerializeField]
    private object SerializedPrivateField;

    public object SerializedField;

    [OdinSerialize]
    public object SerializedProperty { get; set; }
}

If this doesn't work for you, it is very easy to implement the serialization yourself, by implementing Unity's ISerializationCallbackReceiver interface, and using Odin's UnitySerializationUtilities class.


[ShowOdinSerializedPropertiesInInspector]
public class YourMonoBehaviour : MonoBehaviour, ISerializationCallbackReceiver
{
    [SerializeField, HideInInspector, ExcludeDataFromInspector]
    private SerializationData serializationData;

    void ISerializationCallbackReceiver.OnAfterDeserialize()
    {
        UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData);
    }

    void ISerializationCallbackReceiver.OnBeforeSerialize()
    {
        UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData);
    }
}

Note the ShowOdinSerializedPropertiesInInspector attribute that the class has been decorated with. This tells Odin that the type is being specially serialized, and makes sure that all the values that are now serialized will show up in the inspector.

Also note the ExcludeDataFromInspector attribute; this attribute is not strictly necessary, but indicates to the Odin inspector that the member should be ignored utterly. When there are large amounts of serialized data inside the member, telling Odin to ignore it completely can make quite a difference in performance.

If you want prefab value support, you also need to implement the ISupportsPrefabSerialization interface, which is also quite simple.


[ShowOdinSerializedPropertiesInInspector]
public class YourMonoBehaviour : MonoBehaviour, ISerializationCallbackReceiver, ISupportsPrefabSerialization
{
    [SerializeField, HideInInspector, ExcludeDataFromInspector]
    private SerializationData serializationData;

    SerializationData ISupportsPrefabSerialization.SerializationData { get { return this.serializationData; } set { this.serializationData = value; } }

    void ISerializationCallbackReceiver.OnAfterDeserialize()
    {
    UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData);
    }

    void ISerializationCallbackReceiver.OnBeforeSerialize()
    {
    UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData);
    }
}

And that's it! Your type now has full Odin serialization support.

The difference between OdinSerialize and ShowInInspector

There is a very big difference between [OdinSerialize] and [ShowInInspector], though it may not be obvious at first. They both usually cause something to be shown in the inspector, but with [ShowInInspector], whatever is shown will not be saved. Use [OdinSerialize] if you want to both show something in the inspector, and have it be saved. If you've put [OdinSerialize] on a member, and it still doesn't show up in the inspector, it is because the attribute is not actually causing the member to be serialized - for example, a non-auto property or a virtual or abstract auto-property will never be serialized by Odin. Nor will a property which is also declared on an interface that is implemented by the defining class, or one of its inherited classes (as this causes the property methods to become virtual).

You can combine [HideInInspector] and [OdinSerialize], if you want something saved, but now shown in the inspector.

Avoiding Unity's Infinite Depth Warning

By default, Unity doesn't support serializing references, polymorphism, or null values. This means they need to protect themselves against infinite depth serialization loops, where a type contains a value of its own type. They scan for types they might serialize, that contain themselves, and then give you a warning in the console.

Odin, however, does support serializing references, polymorphism, and null values, and Unity's warning can be quite inconvenient to get rid of. We have introduced a work-around to get rid of it, while still telling Odin to serialize the value. It is not pretty, but it is quite simple, and it works.

There is a special case introduced: if a member is marked with both the [NonSerialized] and [OdinSerialize] attributes, the member will be serialized by Odin. This means that Unity will pass your self-referential member by, but Odin will still find it and serialize it.

Features and limitations

Odin's serialization is currently supported on all platforms, save the Universal Windows Platform in non-IL2CPP builds. Note that when using Odin's serialization on IL2CPP or Mono AOT platforms, you have to follow a special AOT procedure to make Odin's serialization work right in builds.

By default, Odin's serialization system will serialize any public field, or any private field marked with [SerializeField], that Unity would not serialize. As such, you generally don't need to think much about it; it'll fill in the gaps where necessary. However, if you do need to make sure a specific member is always serialized by Odin, you can mark any field or auto-property with the Odin-specific [OdinSerialize] attribute to ensure that.

Odin supports serializing a wide range of types, from dictionaries to delegates. In fact, Odin will, by default, attempt to serialize pretty much anything you hand it. In most cases, this will work fine, and in some cases it won't. For the cases where it won't, it is possible to manually extend the serialization so it does. (See Creating a new type formatter.)

The serialization system supports a range of data formats, and also lets you create your own. By default, Odin comes with three formats: binary, json, and nodes.

Binary

The binary format is to be used when performance is required; it is optimized for speed and for keeping garbage allocations at a minimum. By default, it will be used in play mode, and in builds.

Json

The json format is never used by default, but is supported for the cases where you want to do manual serialization of something and you want the serialized data to be human-readable. It is important to note that the json format does not have optimal performance, and allocates a lot of unnecessary garbage in the current version of Odin. Longer term, some performance improvements are in order. For now, only use the json format where it is necessary.

Nodes

This format is the default in the editor, while not in play mode. It is a special format that doesn't serialize to a stream of bytes, but instead to a list of nodes that Unity then serializes - it is meant to be used in conjunction with the Unity editor's text asset serialization. When used with Unity's text format, the node data will be formatted in the file such that it is mergeable in source control, making this format advisable for use in projects with multiple collaborators. The node format is generally not very performant, and is by default only allowed to be used in the editor.

Serialization on IL2CPP/Mono AOT

On AOT-compiled platforms, Unity's code stripping can remove classes that the serialization system needs, or fail to generate code for necessary variants of generic types. Therefore, Odin can create an assembly that directly references all the serialization functionality that is needed at runtime, to ensure that it will be avaible in the final AOT build.

To create this assembly, first go to Tools -> Odin Inspector -> Preferences -> Serialization -> AOT Generation. Click "Scan Project", and wait for the scan to complete. Essentially, during the scan, Odin loads and saves every relevant asset and scene in your entire project, and meanwhile it keeps track of which types are being saved by Odin's serialization system. From this information, it then creates a list of all types that it must support serializing in AOT builds. You can modify this list of types, adding to it or disabling support for types as you wish - when Odin scans, it will leave all of your custom modifications to the list of types alone. However, unless you know what you're doing, it is likely best to just use the base list of supported types that Odin generates itself from the scan.

Finally, to generate the assembly itself, press the "Generate DLL" button, and wait for Unity to reload. You should do this every time before you build. You must always ensure that your generated AOT dll is up to date before you make an AOT build, or serialization may fail in your build.

If you leave the generated .dll in your project after building, some versions of Unity may start throwing errors related to the generated .dll in the console on script reloads. This happens, for example, when you have generated a .dll that contains support for a custom type of yours, and you then change the name of that type, or delete it. This causes Unity to be unable to resolve the types that the .dll references, and therefore, it will throw errors. These error messages are harmless, but annoying. Therefore, you may want to establish a habit of deleting the generated .dll after you've made a build and are done with it.

Words of caution

The serialization system is of course designed to be as durable and robust as we could make it. When set to resilient mode, it is very fault tolerant, handles data corruption fairly well, and also tolerates data types changing under its feet. For example, should you change a Vector3 field to a Vector4 field, your old x,y,z values will be retained. We have done our very best to make it a reliable system.

Even so, using Odin's serialization means making yourself very dependent on a complicated system, and also makes it far harder to remove Odin from your project, should you ever wish to. (See Uninstalling Odin and Best Practices)

It also means sacrificing some performance for the increased flexibility you gain. When using the binary format, Odin's serialization is fast, but since it has to piggyback on Unity's serialization, it literally cannot be quite as fast as Unity's.

Additionally, it is very easy to let the freedom of Odin's serialization system get to your head, and quickly overcomplicate things. It is very easy to get carried away and shoot yourself in the foot. There are strong arguments to be made for keeping your data structures as simple as possible, and polymorphism, nested dictionaries and cyclic graphs are all but simple. We believe there are instances where the added complexity is more than justified - however, there are also cases where it is not, and it is important to keep this in mind.

Finally, making the custom serialization work with prefab instances has necessitated creating our own custom prefab modification system that lies on top of Unity's own, that applies to all inspector-visible data serialized with Odin. While it has seen a lot of beta testing and has been successfully used "in the field", it is possible that there are still subtle issues, so it is advisable to modify specially serialized prefab instances with care and not depend on the custom prefab modification system for critical data until you are confident that it reliably does what you need.

It would therefore be best to avoid using Odin's serialization where it is not needed, as you should always minimize your exposure to possible failure modes, however remote.

Serialization debugging

Odin includes a serialization debugging utility, that can help you out if you're having trouble with values disappearing or not showing up in the inspector. The debugger will show you which members of any given type are being serialized, and whether they are serialized by Unity, Odin or both. It will also give you detailed describtion, explaning what what is happening for any given member.

You can access the debug window from Tools > Odin Inspector > Serialization Debugger and select your script type from the dropdown to start debugging. Or you can directly start debugging a component by clicking the Debug Serialization button in the component cogwheel menu.