Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by Marquo · May 11, 2016 at 11:00 AM · serializationdictionaryserializable

Dictionary serialization woes

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[i], values[i]);
         }
     }
 
     [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<Dict>(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");
         }
     }
 }
Comment
Add comment · Show 7
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Munchy2007 · May 11, 2016 at 01:30 PM 1
Share

$$anonymous$$y Generic Serialiser class works with dictionaries.

http://www.doofah.com/tutorials/unity/generic-serialiser-class/

avatar image Marquo Munchy2007 · May 12, 2016 at 07:41 PM 0
Share

I've tried using this class, but I get exactly the same error on this line of LoadFromPlayerPrefs: T deserializedObject = (T)bFormatter.Deserialize(stream);

Here is my code using your class: http://hastebin.com/ewohuqokal.vala

avatar image Munchy2007 Marquo · May 13, 2016 at 11:07 AM 0
Share

You're trying to serialize an instance of your serializable dictionary class. You don't need to make your own version of a dictionary that implements it's own serialization logic.

Just pass an instance of a standard dictionary to my serializer class and it will work.

Here is an example, it creates a new dictionary, saves it to a binary file, then loads it into a new instance and log prints the value of the 1st element.

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
 
 public class SerializeDictionaryTest : $$anonymous$$onoBehaviour {
 
     // Use this for initialization
     void Start () {
         Dictionary<int, string> myDict = new Dictionary<int, string>();
         myDict.Add(1,"1st entry");
         myDict .Add(2,"2nd entry");
 
         Doofah.GenericSerialiser.SaveToBinaryFile(Path.Combine(Application.persistentDataPath, "$$anonymous$$yDictionary.bin"), myDict);
 
         var loadedDict = Doofah.GenericSerialiser.LoadFromBinaryFile<Dictionary<int,string>>(Path.Combine(Application.persistentDataPath, "$$anonymous$$yDictionary.bin"));
 
         Debug.Log(string.Format("Value at element zero = {0}", loadedDict[1]));
     }
 }
avatar image Bunny83 · May 12, 2016 at 09:19 PM 0
Share

Why do you have commented out the parameter less constructor and the serialization constructor? Do they throw any errors? It's possible that you need to implement those in the concrete class ("Dict") as well.

$$anonymous$$eep in $$anonymous$$d that you don't need that "SerializableDictionary" class unless you want to have Unity serialize that dictionary inside the editor so you can load it at runtime.

If you just want to use a Dictionary at runtime to send data over the network, just use an ordinary Dictionary which should be serializable by default (serializable by the BinaryFormatter, not by Unity).

avatar image Marquo Bunny83 · May 12, 2016 at 10:12 PM 0
Share

I'll have a go rewriting the using ISerializable. I commented the ctor out as it didn't fix the problem, and I was trying to get rid of everything that wasn't required.

avatar image Bunny83 Marquo · May 12, 2016 at 10:34 PM 0
Share

Have you read my suggestion to actually implement those constructor in the Dict class? Since in the end that's the one that need to be created by the deserializer and the error complains that it can't find a proper constructor.

Constructors are not inherited with the exception of the parameterless constructor (if available). Any specialized constructors are not propergated to derived classes. So you need to implement those explicitly in those classes.

But again, i see no reason why using the Dict / SerializableDictionary class in the first place. They just add unnecessary overhead when you want to send it over the network.

avatar image Marquo Bunny83 · May 12, 2016 at 10:27 PM 0
Share

This version still hits the same issue: http://hastebin.com/puqukokado.vala

2 Replies

· Add your reply
  • Sort: 
avatar image
0
Best Answer

Answer by Bunny83 · May 12, 2016 at 10:50 PM

As i said in the comment, this fixes the issue:

 [Serializable]
 class Dict : SerializableDictionary<int, string>
 {
     public Dict():base() { }
     public Dict(SerializationInfo info, StreamingContext context) : base(info, context) { }
 }

I just tested it in Unity.

The version you posted on hastebin doesn't implement any constructor in the Dict class. Again constructors are not inherited.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Marquo · May 13, 2016 at 07:51 PM 0
Share

Sorry, I see your point. I had tried every part of what was needed, but not all together :$

$$anonymous$$any thanks for the help!

avatar image
0

Answer by Marquo · May 12, 2016 at 08:25 PM

In case anyone else finds this same issue, I believe my problem is that ISerializationCallbackReceiver is an editor only interface, so these functions don't work at runtime.

In the editor Munchy2007's class will work great

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · May 12, 2016 at 09:11 PM 0
Share

ISerializationCallbackReceiver is not an editor only interface. It's true that "OnBeforeSerialize" is only called inside the editor since that's the only place where the Unity serializer actually serializes any data. But "OnAfterDeserialize" has to work at runtime in order to actually read the serialized assets that has been packed into the game.

You don't seem to have problems with Unity's serialization system but with the usual .NET BinarySerializer

avatar image Marquo Bunny83 · May 12, 2016 at 09:18 PM 0
Share

So how would I go about making a class serialise/deserialise using the BinaryFormatter/$$anonymous$$emoryStream shown in the code?

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

5 People are following this question.

avatar image avatar image avatar image avatar image avatar image

Related Questions

Help with editor serialization 1 Answer

In this case the lists do not serialize. Why? 2 Answers

JsonUtility not found / working 0 Answers

How to export Serialized Data with my Build 0 Answers

Serialized class, Instances and access 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges