Serializing subclass of generic class not working

I’m trying to serialize a generic class by creating special subclasses as mentioned in many placed (even the documentation for UnityEvent) but it’s not working as expected.

[Serializable]
public class SceneData
{
    // Need to create subclasses to be able to serialize
    [Serializable]
    private class PersistentBoolCollection : PersistentItemDataCollection<bool> { }
    
    [SerializeField]
    private PersistentBoolCollection persistentBools;
}

[Serializable]
public class PersistentItemDataCollection<T> : ISerializationCallbackReceiver
	where T : struct
{
	// Use special data type for serialization
	[Serializable]
	private struct SerializableItemData
	{
		public string SceneName;
		public string ID;
		public T Value;
	}

	[SerializeField]
	private List<SerializableItemData> serializedList = new List<SerializableItemData>();

	[NonSerialized]
	private Dictionary<string, Dictionary<string, PersistentItemData<T>>> scenes
		= new Dictionary<string, Dictionary<string, PersistentItemData<T>>>();

	public void OnAfterDeserialize()
	{
		// Convert itemData to dictionary for fast runtime access
		scenes = new Dictionary<string, Dictionary<string, PersistentItemData<T>>>(serializedList.Count);
		foreach (var serializableItem in serializedList)
		{
			scenes[serializableItem.SceneName][serializableItem.ID] = new PersistentItemData<T>
			{
				SceneName = serializableItem.SceneName,
				ID = serializableItem.ID,
				Value = serializableItem.Value
			};
		}
	}

	public void OnBeforeSerialize()
	{
		// Dictionaries can't be serialized so convert to list first
		serializedList = new List<SerializableItemData>();
		foreach (var scene in scenes)
		{
			foreach (var id in scene.Value)
			{
				serializedList.Add(new SerializableItemData
				{
					SceneName = scene.Key,
					ID = id.Key,
					Value = id.Value.Value
				});
			}
		}
	}

	public void SetValue(PersistentItemData<T> itemData)
	{
		if (!scenes.ContainsKey(itemData.SceneName))
			scenes[itemData.SceneName] = new Dictionary<string, PersistentItemData<T>>();

		scenes[itemData.SceneName][itemData.ID] = itemData;
	}

	public bool TryGetValue(string sceneName, string id, out PersistentItemData<T> value)
	{
		if (scenes.ContainsKey(sceneName) && scenes[sceneName].ContainsKey(id))
		{
			value = scenes[sceneName][id];
			return true;
		}
		else
		{
			value = default(PersistentItemData<T>);
			return false;
		}
	}
}

When using JsonUtility to serialize SceneData the output is:

"sceneData":{"persistentBools":{},"persistentInts":{}}

I confirmed that there were 24 items in the list after the OnBeforeSerialize callback, so I’m not sure what is going wrong here.

Turns out Unity will only serialize generic types that are one level deep. It’s not ideal, but a workaround is to make the subclass another public class and pass it in:

	[Serializable]
	public class SerializableItemData<T>
	{
		public string SceneName;
		public string ID;
		public T Value;
	}

	[Serializable]
	public class PersistentItemDataCollection<T, U> : ISerializationCallbackReceiver
		where T : struct
		where U : SerializableItemData<T>, new()
	{
		[SerializeField]
		private List<U> serializedList = new List<U>();

		[NonSerialized]
		private Dictionary<string, Dictionary<string, PersistentItemData<T>>> scenes
			= new Dictionary<string, Dictionary<string, PersistentItemData<T>>>();
    }

And then use it like so:

[Serializable] public class SerializableBoolData : SerializableItemData<bool> { }
[Serializable] private class PersistentBoolCollection : PersistentItemDataCollection<bool, SerializableBoolData> { }

[Serializable] public class SerializableIntData : SerializableItemData<int> { }
[Serializable] private class PersistentIntCollection : PersistentItemDataCollection<int, SerializableIntData> { }
#endregion

[SerializeField]
private PersistentBoolCollection persistentBools;
public PersistentItemDataCollection<bool, SerializableBoolData> PersistentBools { get { return persistentBools; } }

[SerializeField]
private PersistentIntCollection persistentInts;
public PersistentItemDataCollection<int, SerializableIntData> PersistentInts { get { return persistentInts; } }