• 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 manu3d · Sep 08, 2013 at 08:28 PM · serializationdictionaryfile-io

Simplest Dictionary Serialization to a File?

What's the simplest way to save/load a dictionary to/from a file, bypassing Unity's built-in serialization? Specifically, I'd like my data to persist while switching from editor mode to player mode and back.

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 Fattie · Sep 09, 2013 at 08:12 AM 0
Share

Just TBC you or someone reading may not know about the famous http://whydoidoit.com/unityserializer/ which is great (and even free!)

avatar image manu3d · Sep 09, 2013 at 12:58 PM 0
Share

I am aware of it and as far as I know quite a few people are using it successfully. However, I do not find it particularly intuitive. For example its tutorial bases everything on interaction with its custom GUIs while I prefer to mostly deal with code for these issues. Also, by my understanding it is "player mode"-oriented, while I needed data-persisting methods capable of switching from editor to player mode and back.

2 Replies

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

Answer by manu3d · Sep 08, 2013 at 09:12 PM

As detailed in this answer, I was having a little too much trouble saving/loading the dictionary data to/from lists using Unity's built-in serializer. Other people use this method routinely it seems, but by my understanding the unity-serialized data survives going from Editor mode to Player mode but not viceversa. I therefore decided to bypass unity's serialization completely.

As such, here is a script that no matter how unrealistically simple, inherits from MonoBehaviour and allows the data in the dictionary to survive switching from Editor mode to Player mode and viceversa. I post it here, with its considerable limitations, in the hope it might provide a starting point for others.

The main class follows. To use it, just drag and drop it onto the components list of a GameObject, i.e. a default cube. Notice that, strictly speaking, it is not the simplest possible code as it includes some demo functionality to populate the dictionary. You might want to replace that code with something closer to your game's usage scenarios.

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
 
 [ExecuteInEditMode]
 public class PersistentDictionaryExample : MonoBehaviour 
 {
     private Dictionary<string, string> theDictionary;
     private string prefix;
     
     private static string savedDataPath = Application.persistentDataPath + "/savedData";
     private static string dictionaryName = "PersistentDictionaryExample.txt";
     private static string dictionaryFullName = savedDataPath + "/" + dictionaryName;
     
     private string checkMode()
     {
         if(Application.isPlaying)
             return "PlayMode::";
         else
             return "EditMode::";
     }
     
     // in OnEnable() we load the content of the dictionary from file
     void OnEnable() 
     {
 
         theDictionary = new Dictionary<string, string>();
         
         // CreateDirectory() checks for existence and 
         // automagically creates the directory if necessary
         Directory.CreateDirectory(savedDataPath);
         
         // the file is a simple key,value list, one dictionary item per line
         if(File.Exists(dictionaryFullName))
         {
             string[] fileContent = File.ReadAllLines(dictionaryFullName);
             
             foreach(string line in fileContent)
             {
                 string[] buffer = line.Split(',');
                 if(buffer.Length == 2)
                     theDictionary.Add(buffer[0], buffer[1]);
             }
             
             Debug.Log(checkMode()+"Awake()::LOADED DATA from "+dictionaryFullName);
         }        
     }
     
     // in OnDisable() we save the content of the dictionary to file.
     public void OnDisable()
     {
 
         if(theDictionary != null)
         {
             string fileContent = "";
             
             foreach(var item in theDictionary)
             {
                 fileContent += item.Key + "," + item.Value + "\n";
             }
             
             File.WriteAllText(dictionaryFullName, fileContent);
             
             Debug.Log(checkMode()+"OnDisable()::SAVED DATA in "+dictionaryFullName);
         }        
         
         theDictionary = null;
     }
     
     // to simulate modifications to the dictionary in play mode
     // we simply keep count of the number of update calls since 
     // this object was created. In Edit mode the addRandomContent
     // method is triggered manually by the user, via the inspector.
     void Update() 
     {
         if(Application.isPlaying)
         {
             string key = "updates";
             
             if(!theDictionary.ContainsKey(key))
                 theDictionary[key] = "0";
             
             theDictionary[key] = (int.Parse(theDictionary[key]) + 1) + "";    
         }        
     }
     
     // this method is called manually by the user in Editor mode, 
     // through the identically-labelled inspector button. 
     // Along with the Update() method it is used to populate/modify 
     // the dictionary with simple data.
     public void addRandomContent()
     {
         // GetRandomFileName returns a 8.3-characters filename
         string aKey = Path.GetRandomFileName();
         string aValue = Path.GetRandomFileName();
         
         // the dot is removed to obtain a simple 11-characters random string
         aKey = aKey.Replace(".", "");
         aValue = aValue.Replace(".", ""); 
         
         theDictionary.Add(aKey, aValue);    
     }
     
     // not strictly necessary but useful to conveniently list 
     // the content of the dictionary in the inspector
     public Dictionary<string, string>.Enumerator GetEnumerator() 
     {
         if(theDictionary != null)
             return theDictionary.GetEnumerator();
         else 
             return (new Dictionary<string, string>()).GetEnumerator();
     }
 
 }

Some important details: ideally I would place the saving functionality in the OnDestroy() call. In a class supposed to work only in Player mode that would probably work. Unfortunately, as detailed in this answer, while switching from Editor to Player mode, OnDestroy() is invoked after an OnEnable()/OnDisable() "combo" (perhaps part of a recompilation step) that destroys the dictionary. As such I was left with no choice but save the data in OnDisable() as it is invoked once before the combo.

Alongside the main class is a small inspector, to be placed in Assets/Editor, to keep an eye on the content of the dictionary and manually populate it with some random data.

 using UnityEngine;
 using UnityEditor;
 using System.Collections;
 
 [ExecuteInEditMode]
 [CustomEditor (typeof(PersistentDictionaryExample))]
 public class PersistentDictionaryExampleInspector : Editor 
 {
     private PersistentDictionaryExample theDictionary;
     
     void Awake()
     {
         theDictionary = (PersistentDictionaryExample) target;    
     }
     
     public override void OnInspectorGUI() 
     {
         GUILayout.BeginVertical();
         
             if(GUILayout.Button("Add Random Pair"))
             {
                 theDictionary.addRandomContent();
             }
     
             GUILayout.BeginHorizontal();
                 EditorGUILayout.LabelField("keys");
                 EditorGUILayout.LabelField("values");
             GUILayout.EndHorizontal();
         
             EditorGUILayout.Separator();
         
             foreach(var item in theDictionary)
             {
                 GUILayout.BeginHorizontal();
                 
                     EditorGUILayout.LabelField(item.Key);
                     EditorGUILayout.LabelField(item.Value);
             
                 GUILayout.EndHorizontal();
             }
         
         GUILayout.EndVertical();
     }
 }



Pitfalls

Obviously, the PersistentDictionary class is very limited, it's only meant to be a simple example. Apart from the lack of proper file I/O checks and error handling, one of its biggest problem from my point of view is that it can't be used on multiple objects as they'd all write to the same file. Short of writing one file per object using some kind of id to track the relationship in the filename, some kind of I/O data manager might be needed. I.e. the data could actually be on a single data manager while being accessed by the scripts/inspectors on individual objects. Alternatively, the data could be on the individual objects after all, loaded from the data manager in OnEnable() and stored back in OnDisable(). The data manager in both cases would then be responsible for the actual file I/O rather than the individual objects.

Comments and feedback are appreciated.

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
avatar image
1

Answer by vexe · Jan 04, 2015 at 12:15 PM

Dictionaries serialization/exposure along with many other types is what VFW offers, best of all it's free. All your custom data is serialized and persist during assembly reloads.

Saving something to file is a walk in the park!

 public class SaveMyDic : BetterBehaviour
 {
     public Dictionary<string, List<int>> myDict = new Dictionary<string, List<int>>();
     public string fileName = "dict.bin";
 
     string dictPath { get { return Application.dataPath + "/" + fileName; } }
 
     [Show] void Save()
     {
         string serializedData = Serializer.Serialize(myDict);
         using (var writer = new StreamWriter(File.Open(dictPath, FileMode.OpenOrCreate)))
         {
             writer.WriteLine(serializedData);
         }
     }
 
     [Show] void Load()
     {
         using (var reader = new StreamReader(File.OpenRead(dictPath)))
         {
             string serializedData = reader.ReadLine();
             myDict = Serializer.Deserialize<Dictionary<string, List<int>>>(serializedData);
         }
     }
 }



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

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.

Welcome to Unity Answers

The best place to ask and answer questions about development with Unity.

To help users navigate the site we have posted a site navigation guide.

If you are a new user to Unity Answers, check out our FAQ for more information.

Make sure to check out our Knowledge Base for commonly asked Unity questions.

If you are a moderator, see our Moderator Guidelines page.

We are making improvements to UA, see the list of changes.



Follow this Question

Answers Answers and Comments

16 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

Related Questions

Reference to value type 1 Answer

How can I save a Dictionary in a class to JSON? 2 Answers

Nested Dictionary data lost after playmode 0 Answers

Serializing Dictionary with Vector3 keys 1 Answer

Dictionairy Serialization Problem: Keys and values get lost 0 Answers

  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges