Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
13 Jun 22 - 14 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
15
Question by Iwa · May 21, 2013 at 02:42 PM · serializationscriptableobjectdictionary

[Solved]How to serialize Dictionary with Unity Serialization System

Edit: http://forum.unity3d.com/threads/finally-a-serializable-dictionary-for-unity-extracted-from-system-collections-generic.335797/

Hello everyone,

I already read many things about Serialization with Unity and i already know that Dictionary are not serialized by Unity. But I need to use Dictionaries (or something similar) and to serialized data they contains.

I have tried something like this :

 sing UnityEngine;
 using System;
 using System.Collections;
 using System.Collections.Generic;
 
 [Serializable]
 public class Map<TKey, TValue>{
     
     [SerializeField]
     private List<TKey> keysList = new List<TKey>();
     public List<TKey> KeysList
     {
         get{return keysList;}
         set{keysList=value;}
     }
 
     [SerializeField]
     private List<TValue> valuesList = new List<TValue>();
     public List<TValue> ValuesList
     {
         get{return valuesList;}
         set{valuesList=value;}
     }
     
     private Dictionary<TKey, TValue> dictionaryData = new Dictionary<TKey, TValue>();
     public Dictionary<TKey, TValue> DictionaryData
     {
         get{return dictionaryData;}
         set{dictionaryData =value;}
     }
     
     public void Awake()
     {
         try{
             
             for(int i =0; i<keysList.Count;i++)
             {
                 dictionaryData.Add(keysList[i], valuesList[i]);
             }
             
         }
         catch(Exception)
         {
             Debug.LogError("KeysList.Count is not equal to ValuesList.Count. It shouldn't happen!");
         }
         
     }
     
     public void OnEnable ()
     {
 
       Debug.Log("totototo");
         
     }
     
     public void Add(TKey key, TValue data)
     {
         dictionaryData.Add(key, data);
         keysList.Add(key);
         valuesList.Add(data);
     }
     
     public void Remove(TKey key)
     {        
         valuesList.Remove(dictionaryData[key]);
         keysList.Remove(key);
         dictionaryData.Remove(key);
         
     }
     
     public bool ContainsKey(TKey key)
     {
         return DictionaryData.ContainsKey(key);
     }
     
     public bool ContainsValue(TValue data)
     {
         return DictionaryData.ContainsValue(data);
     }
     
     public void Clear()
     {
         DictionaryData.Clear();
         keysList.Clear();
         valuesList.Clear();
     }

The problem is that none of my variables are serialized, even the simple List and List. Maybe it's because I don't inherit from ScriptableObject.

But if I inherit from ScriptableObject, could I still use templates? I believe that ScriptableObject can't instantiate a templated class, because I haven't seen anything about this until now. So how can I serialize the data of my Dictionaries?

Can I use a binary serialization for this part? Using both serialization seems to be a little bit weird and unconstant. So any help could be really nice.

Thanks for reading.

Iwa

Comment
Add comment · Show 2
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 Benproductions1 · May 22, 2013 at 01:32 AM 0
Share

This is a very good question, BU$$anonymous$$P

avatar image EyePD · Jun 21, 2017 at 03:23 PM 0
Share

There's an open Feedback issue for dictionary serialization, please consider voting for it: https://feedback.unity3d.com/suggestions/serialize-generic-dictionary

6 Replies

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

Answer by hiddenspring81 · May 23, 2013 at 05:29 PM

Have you seen this question? If you have a generic type Foo

 [Serializable]
 public class Foo<T>
 {
   public List<T> stuff;
 }

And use it in the following way

 public class Foobar : MonoBehaviour
 {
   public Foo<int> baz;
 }

The instance variable baz won't be serializable through the Unity Editor. However, if you create a non-generic subclass of Foo, it will be serializable

 public class Foobar : MonoBehaviour
 {
   [Serializable]
   public class FooInt : Foo<int> { }
   public FooInt baz;
 }

To be honest, I'm really not sure why this works, but I've used this hack on a number of occasions, to serialize generic types through the Editor. In your case, you would create a non-generic subclass of Map, for each combination of TKey and TValue that you plan on using, and then use the non-generic subclass in your code so that it can be serialized.

Comment
Add comment · Show 8 · 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 TashaSkyUpchurch · May 05, 2019 at 03:45 PM 0
Share

no longer working in 2018.3.14

avatar image Bunny83 TashaSkyUpchurch · May 06, 2019 at 06:13 AM 0
Share

Of course this still works. Unity has some built in types which are designed to be used that way. For example the generic UnityEvent types.


If it doesn't work for you, you either use types which are not supported or one of your base classes is not marked as Serializable. If you need help with your specific problem please ask a seperate question and do not bump old answered questions.

avatar image roberto_sc Bunny83 · Jan 29, 2020 at 11:10 AM 0
Share

No, it doesn't.

Show more comments
avatar image Undertaker-Infinity · Aug 02, 2020 at 02:00 AM -1
Share

Adding comment over the fold, so others don't waste time like I did.

This answer does not apply to the OP, which is about Dictionaries. The solution outlined here, will NOT work for Dictionaries.

avatar image Bunny83 Undertaker-Infinity · Aug 02, 2020 at 10:40 AM 0
Share

It does apply to the OP. Have you read the OP in its entirety? The OP clearly states that he's well aware of the fact that Unity can't serialize dictionaries and he needs an alternative. The actual issues he has is, i quote:

The problem is that none of my variables are serialized, even the simple List and List.

He already uses a replacement for the Dictionary class. However he just created another generic class. Unity (currently) can't handle generic classes. You have to create a concrete non-generic implementation of that $$anonymous$$ap class.


So next time I would recommend to first read the question in order to understand the answer ^^. The answer 42 doesn't help anyone without knowing the question -.-

avatar image SpicyCatGames · Aug 02, 2021 at 01:16 PM 0
Share

This works as of 2019.3 I commented this because people kept saying it doesn't work when it clearly works. Remember to use the attribute System.Serializable.

avatar image
59

Answer by christophfranke123 · Oct 14, 2014 at 07:59 PM

Although there already is an accepted answer, I would like to share solution that is better in my opinion. It uses the ISerializeCallback interface to implement the serialization functionality into a class derived from the Dictionary.

 [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>();
     
     // 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]);
     }
 }



In order to work though, you still have to derive a non generic class from this one:

 [Serializable] public class DictionaryOfStringAndInt : SerializableDictionary<string, int> {}

This derived class now works exactly like a standard dictionary, but is serializable. However, keep in mind that the key type and value type must be serializable themself.

Comment
Add comment · Show 13 · 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 Zoodinger · Oct 28, 2014 at 01:53 AM 1
Share

This solution is perfect, I'm borrowing this. I did not know of ISerializationCallbackReceiver, it looks like this will save me a lot of future headaches.

I modified it slightly myself to avoid copying UnityEngine.Objects that have been destroyed in the event that they're used as a key:

 if (typeof(T$$anonymous$$ey).IsSubclassOf(typeof(UnityEngine.Object)) || typeof(T$$anonymous$$ey) == typeof(UnityEngine.Object)) {
     foreach (var element in this.Where(element => element.$$anonymous$$ey != null)) {
         _keys.Add(element.$$anonymous$$ey);
         _values.Add(element.Value);
     }
 } else {
     foreach (var element in this) {
         _keys.Add(element.$$anonymous$$ey);
         _values.Add(element.Value);
     }
 }
avatar image Reimus · Feb 20, 2015 at 06:12 AM 0
Share

Wonderful solution. This works with no extra tinkering. As someone new to C#, could I just add this into it's own C# file and call it from multiple scripts?

avatar image Aggressor · Jul 19, 2015 at 07:14 PM 0
Share

Very helpful. I am going to look at a way to create a custom inspector for this now :)

avatar image mfshaw · Dec 23, 2016 at 10:37 AM 1
Share

When trying to use this I get the error 'The constructor to deserialize an object of type DictionaryOfStringAndInt was not found'. I'm new to serialization and am not sure what this constructor would look like. Can anyone help?

avatar image Bunny83 mfshaw · Dec 23, 2016 at 03:18 PM 0
Share

@mfshaw:
Could it be that you use some sort of .NET serialization like a BinaryFormatter? This question is about serialization inside the Unity editor using Unity's serialization system.

If you get that error from Unity's serialization system, that means you defined a parameterized constructor but no parameterless constructor. Unity needs a parameterless constructor in order to create an instance of the class. If you don't define any constructors yourself, the class automatically gets a public parameterless constructor. However when you implement any other constructor the parameterless constructor isn't generated automatically.

If this doesn't answer your question, please ask a seperate question about your specific case and do not ask questions in comments on other questions / answers. This is just getting messy.

avatar image bellicapax · Feb 27, 2018 at 01:09 AM 1
Share

I edited this to rid myself of errors in the console and keep keys and values together.


You can see source code for the dictionary here.


Just like the original, you have to implement the generic SerializableDictionary, but you'll also have to implement the generic Serializable$$anonymous$$eyValuePair.


You can see code for an example implementation here.

Show more comments
avatar image
6

Answer by vexe · Jan 04, 2015 at 11:00 AM

EDIT: Here's a more solid solution, works with Unity's default serialization http://forum.unity3d.com/threads/finally-a-serializable-dictionary-for-unity-extracted-from-system-collections-generic.335797/


Allow me to share my solution. Serializing and exposing dictionaries along with any generic type is what is offered in VFW - I make use of ISerializationCallbackReceiver and FullSerializer to write a better custom serialization system that could pretty much serialize anything (interfaces, generics, properties, etc)

If you still need to roll with Unity's serialization, you could try implementing a dictionary with two serializable lists, while there's no hashing, but ya know, it works.

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 Xelnath · Mar 19, 2015 at 09:30 AM 0
Share

I've used Vexe for in-game assets just fine.

Haven't tried it yet for serializing to/from a text file. Is that feasible or simple?

avatar image gooopil · Jul 22, 2015 at 02:23 AM 0
Share

Plug and play solution, excellent!

avatar image
1

Answer by JoaquinRD · Mar 30, 2014 at 08:56 PM

Thanks for your answers, Iwa and hiddenspring81.

I also needed to serialize a dictionary, but I generally don't like using parallel sets of information, so I made my own version of the same concept that uses a custom KeyValuePair class to keep both bits of information together. It implements the IDictionary interface for a little extra versatility.

 using UnityEngine;
 using System;
 using System.Collections;
 using System.Collections.Generic;
 
 public abstract class SerializedKeyValuePair<TKey, TValue>
     where TKey : IEquatable<TKey>
 {
     public abstract TKey Key { get; set; }
     public abstract TValue Value { get; set; }
     
     public SerializedKeyValuePair(TKey key, TValue value)
     {
         Key = key;
         Value = value;
     }
     
     public KeyValuePair<TKey, TValue> AsKeyValuePair()
     {
         return new KeyValuePair<TKey, TValue> (Key, Value);
     }
 }
 
 public abstract class SerializedDictionary<TKeyValuePair, TKey, TValue> :
     IDictionary<TKey, TValue>
         where TKeyValuePair : SerializedKeyValuePair<TKey, TValue>
         where TKey : IEquatable<TKey>
 {
     protected abstract List<TKeyValuePair> KeyValuePairs { get; }
     
     public abstract void Add (TKey key, TValue value);
 
     public bool ContainsKey (TKey key)
     {
         foreach (TKeyValuePair kvp in KeyValuePairs)
         {
             if (kvp.Key.Equals(key))
             {
                 return true;
             }
         }
 
         return false;
     }
 
     public bool ContainsValue(TValue value)
     {
         EqualityComparer<TValue> equalityComparer = EqualityComparer<TValue>.Default;
 
         foreach (TKeyValuePair kvp in KeyValuePairs)
         {
             if (equalityComparer.Equals(kvp.Value, value))
             {
                 return true;
             }
         }
         
         return false;
     }
 
     public bool Remove (TKey key)
     {
         for (int i = 0; i < KeyValuePairs.Count; i++)
         {
             TKeyValuePair kvp = KeyValuePairs[i];
             
             if (kvp.Key.Equals(key))
             {
                 KeyValuePairs.RemoveAt(i);
                 return true;
             }
         }
         
         return false;
     }
 
     public bool TryGetValue (TKey key, out TValue value)
     {
         foreach (TKeyValuePair kvp in KeyValuePairs)
         {
             if (kvp.Key.Equals(key))
             {
                 value = kvp.Value;
                 return true;
             }
         }
         
         value = default(TValue);
         return false;
     }
 
     public TValue GetValue(TKey key)
     {
         foreach (TKeyValuePair kvp in KeyValuePairs)
         {
             if (kvp.Key.Equals(key))
             {
                 return kvp.Value;
             }
         }
         
         throw new ArgumentException("No value was found for the given key");
     }
 
     public void Add (KeyValuePair<TKey, TValue> item)
     {
         Add (item.Key, item.Value);
     }
 
     public void Clear ()
     {
         KeyValuePairs.Clear ();
     }
 
     public bool Contains (KeyValuePair<TKey, TValue> item)
     {
         foreach (TKeyValuePair kvp in KeyValuePairs)
         {
             if (kvp.Key.Equals(item.Key))
             {
                 return EqualityComparer<TValue>.Default.Equals(kvp.Value, item.Value);
             }
         }
         
         return false;
     }
 
     public void CopyTo (KeyValuePair<TKey, TValue>[] array, int arrayIndex)
     {
         for (int i = 0; i < KeyValuePairs.Count; i++)
         {
             TKeyValuePair kvp = KeyValuePairs[i];
             array[i + arrayIndex] = new KeyValuePair<TKey, TValue>(kvp.Key, kvp.Value);
         }
     }
 
     public bool Remove (KeyValuePair<TKey, TValue> item)
     {
         if (Contains(item))
         {
             Remove(item.Key);
             return true;
         }
 
         return false;
     }
 
     public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator ()
     {
         foreach(TKeyValuePair kvp in KeyValuePairs)
         {
             yield return new KeyValuePair<TKey, TValue>(kvp.Key, kvp.Value);
         }
         
         yield break;
     }
 
     IEnumerator IEnumerable.GetEnumerator ()
     {
         return GetEnumerator ();
     }
 
     public TValue this[TKey key]
     {
         get { return GetValue(key); }
         set
         {
             for (int i = 0; i < KeyValuePairs.Count; i++)
             {
                 if (KeyValuePairs[i].Key.Equals(key))
                 {
                     KeyValuePairs[i].Value = value;
                     return;
                 }
             }
             
             Add(key, value);
         }
     }
 
     public ICollection<TKey> Keys {
         get
         {
             List<TKey> list = new List<TKey>();
 
             foreach (TKeyValuePair kvp in KeyValuePairs)
             {
                 list.Add(kvp.Key);
             }
 
             return list;
         }
     }
 
     public ICollection<TValue> Values {
         get
         {
             List<TValue> list = new List<TValue>();
             
             foreach (TKeyValuePair kvp in KeyValuePairs)
             {
                 list.Add(kvp.Value);
             }
             
             return list;
         }
     }
 
     public int Count { get { return KeyValuePairs.Count; } }
 
     public bool IsReadOnly { get { return false; } }
 
     public Dictionary<TKey, TValue> ToDictionary()
     {
         Dictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue> ();
         
         foreach (TKeyValuePair kvp in KeyValuePairs)
         {
             dictionary.Add(kvp.Key, kvp.Value);
         }
         
         return dictionary;
     }
 }

Then, I had to create sub-classes for each, based on the information being serialized. In my case, the Key type was a struct, so leaving the serialization up to the sub-class was handy since Unity doesn't serialize structs:

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using System;
 using HexGame;
 
 [System.Serializable]
 public class MySerializedDictionary :
     SerializedDictionary<MySerializedKeyValuePair, MyKey, MyValue>
 {
     [SerializeField] private List<MySerializedKeyValuePair> m_keyValuePairs = new List<MySerializedKeyValuePair>();
 
     protected override List<MySerializedKeyValuePair> KeyValuePairs
     {
         get { return m_keyValuePairs; }
     }
 
     public override void Add (MyKey key, MyValue value)
     {
         if (ContainsKey(key))
         {
             throw new ArgumentException("The dictionary already contains an entry with the specified key");
         }
         else
         {
             KeyValuePairs.Add(new MySerializedKeyValuePair(key, value));
         }
     }
 }
 
 [Serializable]
 public class MySerializedKeyValuePair : SerializedKeyValuePair<MyKey, MyValue>
 {
     [SerializeField] private int m_keyVariableA;
     [SerializeField] private int m_keyVariableB;
 
     [SerializeField] private MyValue m_value;
 
     public override MyKey Key
     {
         get { return new MyKey(m_keyVariableA, m_keyVariableB); }
         set
         {
             m_keyVariableA = value.A;
             m_keyVariableB = value.B;
         }
     }
 
     public override MyValue Value
     {
         get { return m_value; }
         set { m_value = value; }
     }
 
     public MySerializedKeyValuePair(MyKey key, MyValue value)
         : base (key, value)
     {}
 }
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 Benproductions1 · Mar 31, 2014 at 08:56 AM 2
Share

Just something you should note is that what you have there is just a very inefficient list, not an actual dictionary (from an implementation viewpoint).

The advantage of a dictionary.hashtable is the O(1) lookup time. Your implementation has an O(n) lookup time, which defeats the whole use of dictionaries.

You might as well just use a List from the beginning, for all it's worth ;)

avatar image JoaquinRD · Mar 31, 2014 at 08:42 PM 0
Share

You're right that this is just a glorified list without the performance benefit of an actual dictionary, but the problem that it solves is serialization. I am personally only using this as an editor helper class that allows me to store a "dictionary" in the scene file. At runtime, I build an actual dictionary with the ToDictionary method and use that ins$$anonymous$$d.

It's true that I could still just serialize a list and build a dictionary from that, but I prefer to always think of the collection as a dictionary, rather than switching back and forth.

avatar image
0

Answer by Gerappa · Oct 30, 2018 at 07:30 PM

I'm not sure did I solve your problem but I did something like this.

  1. First I created simple class:

    [Serializable] public class Dictionary { public Sprite Sprite; public ItemType ItemType; }

  2. Next I created second class extend by SerializableObject

[CreateAssetMenu(menuName = "Items/RiseableItem")] public class RiseabeItem : ScriptableObject { public List<Dictionary> Dictionaries; }

Then I can do a list of items with specific Key and Values in SerializableObject. This object can be use by many objects on a scane.

Comment
Add comment · 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
  • 1
  • 2
  • ›

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

34 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Serializing Dictionary: Data lost between sessions 0 Answers

please help me, Scriptable object's variable serialization issue 1 Answer

Serialization issue with ScriptableObject 0 Answers

Nested Dictionary data lost after playmode 0 Answers

Serializing Dictionary with Vector3 keys 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