After a lot of fighting with what kinds of data types will and won't serialize in Unity, I have constructed the following guide to share my discoveries with the rest of you in a fairly definitive manner. Hopefully the rest of you won't have to go through quite as much hair-pulling as I did. Code (csharp): using UnityEngine; using System.Collections.Generic; using System; public class SerializationExample : MonoBehaviour { public int publicInt; // YES - public base types will serialise public String publicString; // YES - public object references will serialise private int privateInt; // NO - private fields won't serialise... private String privateString; [SerializeField] private int privateSerializeFieldInt; // YES - ... unless they are specifically labelled [SerializeField] [SerializeField] private String privateSerializeFieldString; public int[] intArray; // YES - arrays of base types and objects will serialize public String[] stringArray; public int[,] multidimensionalArray; // NO - multidimensional arrays won't serialize public int[][] arrayOfArrays; // NO - arrays of arrays won't serialize public List<int> listOfInt; // YES - lists of base types and objects will serialise public List<String> listOfString; public List<List<int>> listOfLists; // NO - lists of lists won't serialize public Dictionary<int, String> dictionary; // NO - dictionaries won't serialize public class UserClass { // NO - user defined classes will not serialise... public int x; public UserClass(int x) { this.x = x; } override public String ToString() { return "" + x; } } public UserClass userClass; [System.Serializable] // YES - ... unless they are marked System.Serializable public class SerializableUserClass { public int x; public SerializableUserClass(int x) { this.x = x; } override public String ToString() { return "" + x; } } public SerializableUserClass serializableUserClass; [System.Serializable] // NO - Structs cannot be made serializable public struct SerializableUserStruct { public int x; public SerializableUserStruct(int x) { this.x = x; } override public String ToString() { return "" + x; } } public SerializableUserStruct serializableUserStruct; [System.Serializable] // NO - User defined generic classes cannot be serialized... public class GenericUserClass<Type> { public Type x; public GenericUserClass(Type x) { this.x = x; } override public String ToString() { return "" + x; } } public GenericUserClass<int> genericUserClass; [System.Serializable]] // YES - ... but subclasses which instantiate the type variable can public class InstantiatedUserClass : GenericUserClass<int> { public InstantiatedUserClass(int x) : base(x) {} }; public InstantiatedUserClass instantiatedUserClass; public void Set(int val) { String s = "" + val; publicInt = val; publicString = s; privateInt = val; privateString = s; privateSerializeFieldInt = val; privateSerializeFieldString = s; intArray = new int[1]; intArray[0] = val; stringArray = new String[1]; stringArray[0] = s; multidimensionalArray = new int[1,1]; multidimensionalArray[0,0] = val; arrayOfArrays = new int[1][]; arrayOfArrays[0] = new int[1]; arrayOfArrays[0][0] = val; listOfInt = new List<int>(); listOfInt.Add(val); listOfString = new List<String>(); listOfString.Add(s); listOfLists = new List<List<int>>(); List<int> list = new List<int>(); list.Add(val); listOfLists.Add(list); dictionary = new Dictionary<int, String>(); dictionary[val] = s; userClass = new UserClass(val); serializableUserClass = new SerializableUserClass(val); serializableUserStruct = new SerializableUserStruct(val); genericUserClass = new GenericUserClass<int>(val); instantiatedUserClass = new InstantiatedUserClass(val); } public void Log() { Debug.Log("publicInt = " + publicInt); Debug.Log("publicString = " + publicString); Debug.Log("privateInt = " + privateInt); Debug.Log("privateString = " + privateString); Debug.Log("privateSerializeFieldInt = " + privateSerializeFieldInt); Debug.Log("privateSerializeFieldString = " + privateSerializeFieldString); Debug.Log("intArray[0] = " + intArray[0]); Debug.Log("stringArray[0] = " + stringArray[0]); try { Debug.Log("multidimensionalArray[0,0] = " + multidimensionalArray[0,0]); } catch (Exception e) { Debug.Log("multidimensionalArray[0,0] -> " + e); } try { Debug.Log("arrayOfArrays[0][0] = " + arrayOfArrays[0][0]); } catch (Exception e) { Debug.Log("arrayOfArrays[0][0] -> " + e); } Debug.Log("listOfInt[0] = " + listOfInt[0]); Debug.Log("listOfString[0] = " + listOfString[0]); try { Debug.Log("listOfLists[0] = " + listOfLists[0]); } catch (Exception e) { Debug.Log("listOfLists[0] -> " + e); } try { Debug.Log("dictionary[" + publicInt + "] = " + dictionary[publicInt]); } catch (Exception e) { Debug.Log("dictionary[" + publicInt + "] -> " + e); } Debug.Log("userClass = " + userClass); Debug.Log("serializableUserClass = " + serializableUserClass); Debug.Log("serializableUserStruct = " + serializableUserStruct); Debug.Log("genericUserClass = " + genericUserClass); Debug.Log("instantiatedUserClass = " + instantiatedUserClass); } }
I've serialized struct and lists of lists fine before. Also here is a fully serializable dictionary for anyone who might need. Code (csharp): using System; using System.Runtime.Serialization; using System.Xml; using System.Xml.Serialization; using System.Collections.Generic; using System.Text; [Serializable()] public class SerializableDictionary<TKey, TVal> : Dictionary<TKey, TVal>, IXmlSerializable, ISerializable { #region Constants private const string DictionaryNodeName = "Dictionary"; private const string ItemNodeName = "Item"; private const string KeyNodeName = "Key"; private const string ValueNodeName = "Value"; #endregion #region Constructors public SerializableDictionary() { } public SerializableDictionary(IDictionary<TKey, TVal> dictionary) : base(dictionary) { } public SerializableDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { } public SerializableDictionary(int capacity) : base(capacity) { } public SerializableDictionary(IDictionary<TKey, TVal> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer) { } public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer) { } #endregion #region ISerializable Members protected SerializableDictionary(SerializationInfo info, StreamingContext context) { int itemCount = info.GetInt32("ItemCount"); for (int i = 0; i < itemCount; i++) { KeyValuePair<TKey, TVal> kvp = (KeyValuePair<TKey, TVal>)info.GetValue(String.Format("Item{0}", i), typeof(KeyValuePair<TKey, TVal>)); this.Add(kvp.Key, kvp.Value); } } void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("ItemCount", this.Count); int itemIdx = 0; foreach (KeyValuePair<TKey, TVal> kvp in this) { info.AddValue(String.Format("Item{0}", itemIdx), kvp, typeof(KeyValuePair<TKey, TVal>)); itemIdx++; } } #endregion #region IXmlSerializable Members void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { //writer.WriteStartElement(DictionaryNodeName); foreach (KeyValuePair<TKey, TVal> kvp in this) { writer.WriteStartElement(ItemNodeName); writer.WriteStartElement(KeyNodeName); KeySerializer.Serialize(writer, kvp.Key); writer.WriteEndElement(); writer.WriteStartElement(ValueNodeName); ValueSerializer.Serialize(writer, kvp.Value); writer.WriteEndElement(); writer.WriteEndElement(); } //writer.WriteEndElement(); } void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) { if (reader.IsEmptyElement) { return; } // Move past container if (!reader.Read()) { throw new XmlException("Error in Deserialization of Dictionary"); } //reader.ReadStartElement(DictionaryNodeName); while (reader.NodeType != XmlNodeType.EndElement) { reader.ReadStartElement(ItemNodeName); reader.ReadStartElement(KeyNodeName); TKey key = (TKey)KeySerializer.Deserialize(reader); reader.ReadEndElement(); reader.ReadStartElement(ValueNodeName); TVal value = (TVal)ValueSerializer.Deserialize(reader); reader.ReadEndElement(); reader.ReadEndElement(); this.Add(key, value); reader.MoveToContent(); } //reader.ReadEndElement(); reader.ReadEndElement(); // Read End Element to close Read of containing node } System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { return null; } #endregion #region Private Properties protected XmlSerializer ValueSerializer { get { if (valueSerializer == null) { valueSerializer = new XmlSerializer(typeof(TVal)); } return valueSerializer; } } private XmlSerializer KeySerializer { get { if (keySerializer == null) { keySerializer = new XmlSerializer(typeof(TKey)); } return keySerializer; } } #endregion #region Private Members private XmlSerializer keySerializer = null; private XmlSerializer valueSerializer = null; #endregion }
I've tried saving serializable data for more than a week now. Today I've tried with the Serializable Dictionary class, to no avail... I have a class which have VERY little: Code (csharp): using System; public class ItemData { public int elementID { get; set; } public int uniqueID { get; set; } public DateTime creationDate { get; set; } public float x { get; set; } public float y { get; set; } } Then in my game I have a List<ItemData> which I collect the information and place in a SerializableDictionary object, then I try to save the Dictionary like this: Code (csharp): public void SerializeObject (SerializableDictionary<int, String> objectToSerialize) { Debug.Log ("SerializeObject..."); Stream stream = File.Open (filePath, FileMode.Create); BinaryFormatter bFormatter = new BinaryFormatter (); bFormatter.Binder = new VersionDeserializationBinder (); bFormatter.Serialize ((Stream)stream, (SerializableDictionary<int, String>)objectToSerialize); stream.Close (); Debug.Log ("SerializeObject DONE"); } And then I get the JIT error in the bFormatter.Serialize call... Anybody can tell me what I am doing wrong??? Thanks in advance, Jay_Santos
tertle's dictionary appears not to work at all, using SerializableDictionary<(serializable thing), (serializable thing)> However, neither does pwelter's SerializableDictionary Is this a bug in Unity itself - something blocking dictionaries from serializing? Or is there some unwritten subtlety you have to do, to make Unity support serialization correctly?
one you can add to you list: Static fields/propperties can not be serialized. I've before had luck in serializing List of Lists or list of classes containing lists, however this may be due to the newest .net framework 4.5 which I believe mono does not yet work with.