In this case the lists do not serialize. Why?

In the following code an attempt is made to use two lists as a natively serializable storage for a dictionary.

A very productive discussion and a number of alternative solutions have been proposed and discussed in this question. However, I’d like this question to focus on why this class doesn’t work rather than finding ways to go around the problem.

Specifically, let’s assume we start with an empty dictionary/lists and in edit mode we fill the dictionary with some values, i.e. through a custom inspector or a simple menu-based function.

First of all, I’m puzzled by the log of calls. The OnDisable/OnEnable pair get called three times, in this order: OnDisable(), OnEnable(), OnDisable(), OnEnable(), OnDisable(), OnEnable(). Why so many times?

More crucially, the first OnDisable() call seems to correctly store the content of the dictionary in the lists, as witnessed by logging the values of keys.Count and values.Count. But the first OnEnable() finds empty lists, as if they hadn’t been serialized! What is going on? Lists, even generic ones, should be automatically serialized!

using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

// the next line is crucial: switching from edit mode to play mode without it
// would only invoke Awake(), and there wouldn't be a chance (i.e. in OnDisable())
// to save the dictionary data into the lists.
[ExecuteInEditMode]

public class MyCustomDict : MonoBehaviour
{

// I know, it'd be better to use a single list:
// bear with me for the sake of simplicity
public List<string> keys = new List<string>();
public List<int> values = new List<int>();
 
// Unity doesn't serialize dictionaries even if you pray in Klingon,
// that's why we need to use the lists.
private Dictionary<string, int> dictionary = new Dictionary<string, int>();
 
public void OnEnable()
{
    Debug.Log("OnEnable!");

    // the following statement says the lists are empty
    // when switching from edit mode to play mode, but a
    // previous invocation of OnDisable() seems to have 
    // saved the content of the dictionary in them!
    Debug.Log("Count:"+keys.Count+"::"+values.Count);

    // here we -should- deserialize the lists and fill the dictionary
    string aKey;
    int aValue;
 
    for(int index = 0; index < keys.Count; index++)
    {
        aKey = keys[index];
        aValue = values[index];
 
        dictionary[aKey] = aValue;
    }

    // here we clear the lists in the hope to free
    // some memory and to clean things up for the
    // time we'll need the lists again
    keys.Clear();
    values.Clear();
}
 
public void OnDisable()
{
    Debug.Log("OnDisable!");

    // here we serialize the content of the dictionary into
    // the lists, so that it gets "indirectly" serialized.
    int index = 0;
    foreach(var item in dictionary)
    {
        keys.Add(item.Key);
        values.Add(item.Value);
    }

    dictionary.Clear();

    // The following statement suggests that the dictionary
    // data has been moved into the lists, but the first
    // invocation to OnEnable() says otherwise.
    Debug.Log("Count:"+keys.Count+"::"+values.Count);
} 

// method to add an item to the dictionary (but not to the lists)
// can be driven by functionality in a custom inspector or in a
// menu-based function
public void AddItem(string aKey, int anInt)
{
     dictionary[aKey] = anInt;

     // this trigger the refresh of a custom inspector
     EditorUtility.SetDirty(this);
}

// method useful for a custom inspector, 
// to show the content of the dictionary
public Dictionary<string, int>.Enumerator GetEnumerator()
{
     return dictionary.GetEnumerator();
}
}

I experimented some, and by serendipity (I never though I’d get to use that word in a sentence) I found out that by commenting out the clearing of the lists in OnEnable, it works fine. I found out because I set them to different sizes, causing an exception whereby they serialized. The only lines not run because of that error were the clearing ones…

Empirical knowledge: If they serialized when moving from game to edit, all the changes you made during game would remain in edit mode. I think it’s possible to have custom serialization from game to edit using the onplaymodechange callback, but that’s still uncofirmed.

I don’t really know of any tutorials… serialization seems to be quite skimpily documented. I did read these pages, among others

However, I think the best way is to get creative with the Unity’s API. I would imagine for custom serialization you’ll at least need

Hi there - maybe looking at this
how-to-serialize-dictionary-with-unity-serialization
may help