I’m trying to serialise a large Dictionary via the network by turning it into a byte array, but I’m falling at the first hurdle or being able to serialise a dictionary to a byte array and back. You could repro my problem making an empty project and attaching the script below to any GameObject.
The deserialise in RtsPlayer.Awake fails with this error:
SerializationException: The constructor to deserialize an object of type Rts.Dict was not found.
I’ve tried the solutions given in every article/question on the subject I could find, some of which are linked in the code, but I hit the same problem every time. Can anyone offer any help?
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.Assertions;
using Rts;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
namespace Rts
{
class BinarySerialisation
{
// Serialisation functions: http://answers.unity3d.com/questions/800344/deserialization-issue-with-dictionary.html
public static byte[] ObjectToByteArray<T>(T obj)
{
MemoryStream m = new MemoryStream();
if (obj != null)
{
BinaryFormatter b = new BinaryFormatter();
b.Serialize(m, obj);
}
return m.ToArray();
}
public static T ByteArrayToObject<T>(byte[] arrBytes)
{
if (arrBytes == null || arrBytes.Length < 1)
return default(T);
BinaryFormatter binForm = new BinaryFormatter();
T obj = (T)binForm.Deserialize(new MemoryStream(arrBytes));
return obj;
}
}
// SerializableDictionary: http://answers.unity3d.com/questions/460727/how-to-serialize-dictionary-with-unity-serializati.html
[Serializable]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
{
[SerializeField]
private List<TKey> keys = new List<TKey>();
[SerializeField]
private List<TValue> values = new List<TValue>();
//public SerializableDictionary() {}
//public SerializableDictionary(SerializationInfo info, StreamingContext context) : base(info, context) {}
// save the dictionary to lists
public void OnBeforeSerialize()
{
keys.Clear();
values.Clear();
foreach(KeyValuePair<TKey, TValue> pair in this)
{
keys.Add(pair.Key);
values.Add(pair.Value);
}
}
// load dictionary from lists
public void OnAfterDeserialize()
{
this.Clear();
if(keys.Count != values.Count)
throw new System.Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are serializable."));
for(int i = 0; i < keys.Count; i++)
this.Add(keys_, values*);*_
* }*
* }*
* [Serializable]*
* class Dict : SerializableDictionary<int, string>{}*
* public class RtsPlayer : MonoBehaviour*
* {*
* void Awake()*
* {*
* Dict simpleDictionary = new Dict();*
* simpleDictionary[7] = “foo”;*
* string foo = simpleDictionary[7];*
* byte[] simpleDictionaryBytes = BinarySerialisation.ObjectToByteArray(simpleDictionary);*
* Dict pendingActionsOut = BinarySerialisation.ByteArrayToObject(simpleDictionaryBytes); // Fails with the following callstack:*
_ /_
_ SerializationException: The constructor to deserialize an object of type Rts.Dict was not found._
_ System.Runtime.Serialization.ObjectRecord.LoadData (System.Runtime.Serialization.ObjectManager manager, ISurrogateSelector selector, StreamingContext context) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.Serialization/ObjectManager.cs:577)_
_ System.Runtime.Serialization.ObjectManager.DoFixups () (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.Serialization/ObjectManager.cs:84)_
_ System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (System.IO.BinaryReader reader) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:145)_
_ System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (BinaryElement elem, System.IO.BinaryReader reader, Boolean readHeaders, System.Object& result, System.Runtime.Remoting.Messaging.Header& headers) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:110)_
_ System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/BinaryFormatter.cs:179)_
_ System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/BinaryFormatter.cs:136)_
_ Rts.BinarySerialisation.ByteArrayToObject[Dict] (System.Byte arrBytes) (at Assets/Scripts/RtsPlayer.cs:35)_
_ Rts.RtsPlayer.Awake () (at Assets/Scripts/RtsPlayer.cs:88)*_
_ */_
* Debug.LogError(“If we got here it worked”);*
* }*
* }*
}