Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Serialization issues

Discussion in 'Scripting' started by malcolmr, Nov 1, 2011.

  1. malcolmr

    malcolmr

    Joined:
    Feb 18, 2010
    Posts:
    51
    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):
    1.  
    2. using UnityEngine;
    3. using System.Collections.Generic;
    4. using System;
    5.  
    6. public class SerializationExample : MonoBehaviour {
    7.    
    8.     public int publicInt;           // YES - public base types will serialise
    9.     public String publicString;         // YES - public object references will serialise
    10.    
    11.     private int privateInt;         // NO - private fields won't serialise...
    12.     private String privateString;
    13.    
    14.     [SerializeField]
    15.     private int privateSerializeFieldInt;   // YES - ... unless they are specifically labelled [SerializeField]
    16.     [SerializeField]
    17.     private String privateSerializeFieldString;
    18.    
    19.     public int[] intArray;          // YES - arrays of base types and objects will serialize
    20.     public String[] stringArray;   
    21.    
    22.     public int[,] multidimensionalArray;    // NO - multidimensional arrays won't serialize
    23.     public int[][] arrayOfArrays;       // NO - arrays of arrays won't serialize
    24.    
    25.     public List<int> listOfInt;     // YES - lists of base types and objects will serialise
    26.     public List<String> listOfString;
    27.    
    28.     public List<List<int>> listOfLists; // NO - lists of lists won't serialize
    29.    
    30.     public Dictionary<int, String> dictionary;  // NO - dictionaries won't serialize
    31.    
    32.     public class UserClass {            // NO - user defined classes will not serialise...
    33.  
    34.         public int x;
    35.        
    36.         public UserClass(int x) {
    37.             this.x = x;
    38.         }
    39.        
    40.         override public String ToString() {
    41.             return "" + x; 
    42.         }
    43.     }
    44.    
    45.     public UserClass userClass;
    46.        
    47.     [System.Serializable]           // YES - ... unless they are marked System.Serializable
    48.     public class SerializableUserClass {
    49.         public int x;
    50.        
    51.         public SerializableUserClass(int x) {
    52.             this.x = x;
    53.         }
    54.  
    55.         override public String ToString() {
    56.             return "" + x; 
    57.         }
    58.     }
    59.    
    60.     public SerializableUserClass serializableUserClass;
    61.    
    62.     [System.Serializable]               // NO - Structs cannot be made serializable
    63.     public struct SerializableUserStruct {
    64.         public int x;
    65.        
    66.         public SerializableUserStruct(int x) {
    67.             this.x = x;
    68.         }
    69.  
    70.         override public String ToString() {
    71.             return "" + x; 
    72.         }
    73.     }
    74.    
    75.     public SerializableUserStruct serializableUserStruct;
    76.        
    77.     [System.Serializable]       // NO - User defined generic classes cannot be serialized...
    78.     public class GenericUserClass<Type> {
    79.         public Type x;
    80.        
    81.         public GenericUserClass(Type x) {
    82.             this.x = x;
    83.         }
    84.  
    85.         override public String ToString() {
    86.             return "" + x; 
    87.         }
    88.     }
    89.    
    90.     public GenericUserClass<int> genericUserClass;
    91.    
    92.     [System.Serializable]]      // YES - ... but subclasses which instantiate the type variable can
    93.  
    94.     public class InstantiatedUserClass : GenericUserClass<int> {
    95.         public InstantiatedUserClass(int x) : base(x) {}
    96.     }; 
    97.     public InstantiatedUserClass instantiatedUserClass;
    98.    
    99.    
    100.     public void Set(int val) {
    101.         String s = "" + val;
    102.        
    103.         publicInt = val;
    104.         publicString = s;
    105.        
    106.         privateInt = val;
    107.         privateString = s;
    108.        
    109.         privateSerializeFieldInt = val;
    110.         privateSerializeFieldString = s;
    111.        
    112.         intArray = new int[1];
    113.         intArray[0] = val;
    114.        
    115.         stringArray = new String[1];
    116.         stringArray[0] = s;
    117.        
    118.         multidimensionalArray = new int[1,1];
    119.         multidimensionalArray[0,0] = val;
    120.        
    121.         arrayOfArrays = new int[1][];
    122.         arrayOfArrays[0] = new int[1];
    123.         arrayOfArrays[0][0] = val;
    124.        
    125.         listOfInt = new List<int>();
    126.         listOfInt.Add(val);
    127.        
    128.         listOfString = new List<String>();
    129.         listOfString.Add(s);
    130.        
    131.         listOfLists = new List<List<int>>();
    132.         List<int> list = new List<int>();
    133.         list.Add(val);
    134.         listOfLists.Add(list);
    135.        
    136.         dictionary = new Dictionary<int, String>();
    137.         dictionary[val] = s;
    138.        
    139.         userClass = new UserClass(val);
    140.         serializableUserClass = new SerializableUserClass(val);
    141.         serializableUserStruct = new SerializableUserStruct(val);
    142.         genericUserClass = new GenericUserClass<int>(val);
    143.         instantiatedUserClass = new InstantiatedUserClass(val);
    144.     }
    145.    
    146.     public void Log() {
    147.         Debug.Log("publicInt = " + publicInt);
    148.         Debug.Log("publicString = " + publicString);       
    149.         Debug.Log("privateInt = " + privateInt);       
    150.         Debug.Log("privateString = " + privateString);     
    151.        
    152.         Debug.Log("privateSerializeFieldInt = " + privateSerializeFieldInt);       
    153.         Debug.Log("privateSerializeFieldString = " + privateSerializeFieldString);     
    154.        
    155.         Debug.Log("intArray[0] = " + intArray[0]);             
    156.         Debug.Log("stringArray[0] = " + stringArray[0]);       
    157.        
    158.         try {
    159.             Debug.Log("multidimensionalArray[0,0] = " + multidimensionalArray[0,0]);       
    160.         }
    161.         catch (Exception e) {
    162.             Debug.Log("multidimensionalArray[0,0] -> " + e);
    163.         }
    164.            
    165.         try {
    166.             Debug.Log("arrayOfArrays[0][0] = " + arrayOfArrays[0][0]);     
    167.         }
    168.         catch (Exception e) {
    169.             Debug.Log("arrayOfArrays[0][0] -> " + e);
    170.         }
    171.        
    172.         Debug.Log("listOfInt[0] = " + listOfInt[0]);       
    173.         Debug.Log("listOfString[0] = " + listOfString[0]);     
    174.        
    175.         try {
    176.             Debug.Log("listOfLists[0] = " + listOfLists[0]);       
    177.         }
    178.         catch (Exception e) {
    179.             Debug.Log("listOfLists[0] -> " + e);
    180.         }
    181.        
    182.         try {
    183.             Debug.Log("dictionary[" + publicInt + "] = " + dictionary[publicInt]);     
    184.         }
    185.         catch (Exception e) {
    186.             Debug.Log("dictionary[" + publicInt + "] -> " + e);
    187.         }
    188.        
    189.         Debug.Log("userClass = " + userClass);     
    190.         Debug.Log("serializableUserClass = " + serializableUserClass);     
    191.         Debug.Log("serializableUserStruct = " + serializableUserStruct);       
    192.         Debug.Log("genericUserClass = " + genericUserClass);       
    193.         Debug.Log("instantiatedUserClass = " + instantiatedUserClass);     
    194.  
    195.     }
    196. }
    197.  
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    I've serialized struct and lists of lists fine before.

    Also here is a fully serializable dictionary for anyone who might need.

    Code (csharp):
    1. using System;
    2. using System.Runtime.Serialization;
    3. using System.Xml;
    4. using System.Xml.Serialization;
    5. using System.Collections.Generic;
    6. using System.Text;
    7.  
    8. [Serializable()]
    9. public class SerializableDictionary<TKey, TVal> : Dictionary<TKey, TVal>, IXmlSerializable, ISerializable
    10. {
    11.     #region Constants
    12.     private const string DictionaryNodeName = "Dictionary";
    13.     private const string ItemNodeName = "Item";
    14.     private const string KeyNodeName = "Key";
    15.     private const string ValueNodeName = "Value";
    16.     #endregion
    17.     #region Constructors
    18.     public SerializableDictionary()
    19.     {
    20.     }
    21.  
    22.     public SerializableDictionary(IDictionary<TKey, TVal> dictionary)
    23.         : base(dictionary)
    24.     {
    25.     }
    26.  
    27.     public SerializableDictionary(IEqualityComparer<TKey> comparer)
    28.         : base(comparer)
    29.     {
    30.     }
    31.  
    32.     public SerializableDictionary(int capacity)
    33.         : base(capacity)
    34.     {
    35.     }
    36.  
    37.     public SerializableDictionary(IDictionary<TKey, TVal> dictionary, IEqualityComparer<TKey> comparer)
    38.         : base(dictionary, comparer)
    39.     {
    40.     }
    41.  
    42.     public SerializableDictionary(int capacity, IEqualityComparer<TKey> comparer)
    43.         : base(capacity, comparer)
    44.     {
    45.     }
    46.  
    47.     #endregion
    48.     #region ISerializable Members
    49.  
    50.     protected SerializableDictionary(SerializationInfo info, StreamingContext context)
    51.     {
    52.         int itemCount = info.GetInt32("ItemCount");
    53.         for (int i = 0; i < itemCount; i++)
    54.         {
    55.             KeyValuePair<TKey, TVal> kvp = (KeyValuePair<TKey, TVal>)info.GetValue(String.Format("Item{0}", i), typeof(KeyValuePair<TKey, TVal>));
    56.             this.Add(kvp.Key, kvp.Value);
    57.         }
    58.     }
    59.  
    60.     void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    61.     {
    62.         info.AddValue("ItemCount", this.Count);
    63.         int itemIdx = 0;
    64.         foreach (KeyValuePair<TKey, TVal> kvp in this)
    65.         {
    66.             info.AddValue(String.Format("Item{0}", itemIdx), kvp, typeof(KeyValuePair<TKey, TVal>));
    67.             itemIdx++;
    68.         }
    69.     }
    70.  
    71.     #endregion
    72.     #region IXmlSerializable Members
    73.  
    74.     void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
    75.     {
    76.         //writer.WriteStartElement(DictionaryNodeName);
    77.         foreach (KeyValuePair<TKey, TVal> kvp in this)
    78.         {
    79.             writer.WriteStartElement(ItemNodeName);
    80.             writer.WriteStartElement(KeyNodeName);
    81.             KeySerializer.Serialize(writer, kvp.Key);
    82.             writer.WriteEndElement();
    83.             writer.WriteStartElement(ValueNodeName);
    84.             ValueSerializer.Serialize(writer, kvp.Value);
    85.             writer.WriteEndElement();
    86.             writer.WriteEndElement();
    87.         }
    88.         //writer.WriteEndElement();
    89.     }
    90.  
    91.     void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
    92.     {
    93.         if (reader.IsEmptyElement)
    94.         {
    95.             return;
    96.         }
    97.  
    98.         // Move past container
    99.         if (!reader.Read())
    100.         {
    101.             throw new XmlException("Error in Deserialization of Dictionary");
    102.         }
    103.  
    104.         //reader.ReadStartElement(DictionaryNodeName);
    105.         while (reader.NodeType != XmlNodeType.EndElement)
    106.         {
    107.             reader.ReadStartElement(ItemNodeName);
    108.             reader.ReadStartElement(KeyNodeName);
    109.             TKey key = (TKey)KeySerializer.Deserialize(reader);
    110.             reader.ReadEndElement();
    111.             reader.ReadStartElement(ValueNodeName);
    112.             TVal value = (TVal)ValueSerializer.Deserialize(reader);
    113.             reader.ReadEndElement();
    114.             reader.ReadEndElement();
    115.             this.Add(key, value);
    116.             reader.MoveToContent();
    117.         }
    118.         //reader.ReadEndElement();
    119.  
    120.         reader.ReadEndElement(); // Read End Element to close Read of containing node
    121.     }
    122.  
    123.     System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
    124.     {
    125.         return null;
    126.     }
    127.  
    128.     #endregion
    129.     #region Private Properties
    130.     protected XmlSerializer ValueSerializer
    131.     {
    132.         get
    133.         {
    134.             if (valueSerializer == null)
    135.             {
    136.                 valueSerializer = new XmlSerializer(typeof(TVal));
    137.             }
    138.             return valueSerializer;
    139.         }
    140.     }
    141.  
    142.     private XmlSerializer KeySerializer
    143.     {
    144.         get
    145.         {
    146.             if (keySerializer == null)
    147.             {
    148.                 keySerializer = new XmlSerializer(typeof(TKey));
    149.             }
    150.             return keySerializer;
    151.         }
    152.     }
    153.     #endregion
    154.     #region Private Members
    155.     private XmlSerializer keySerializer = null;
    156.     private XmlSerializer valueSerializer = null;
    157.     #endregion
    158. }
     
  3. Jay_Santos

    Jay_Santos

    Joined:
    May 31, 2011
    Posts:
    42
    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):
    1. using System;
    2.  
    3. public class ItemData
    4. {
    5.     public int elementID { get; set; }
    6.     public int uniqueID { get; set; }
    7.     public DateTime creationDate { get; set; }
    8.     public float x { get; set; }
    9.     public float y { get; set; }
    10. }
    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):
    1. public void SerializeObject (SerializableDictionary<int, String> objectToSerialize)
    2.     {
    3.         Debug.Log ("SerializeObject...");
    4.         Stream stream = File.Open (filePath, FileMode.Create);
    5.         BinaryFormatter bFormatter = new BinaryFormatter ();
    6.         bFormatter.Binder = new VersionDeserializationBinder ();
    7.         bFormatter.Serialize ((Stream)stream, (SerializableDictionary<int, String>)objectToSerialize);
    8.         stream.Close ();
    9.         Debug.Log ("SerializeObject DONE");
    10.     }
    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
     
  4. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,924
    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?
     
  5. Jacksendary

    Jacksendary

    Joined:
    Jan 31, 2012
    Posts:
    408
    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.