Hi all, I’m banging my head on this c# problem. It might be a character encoding problem with PlayerPrefs. Goal: some structs are marked as [Serializable] so I want to save them out to disk using Unity’s playerprefs.
Here are my 2 methods. The Serialize one is basically lifted from the MSDN example for memorystream usage. The Deserialize is my attempt to write the converse. A couple of observations
they work, provided I use the ASCIIEncoding. If I use the UnicodeEncoding as in the MSDN docs, I get exceptions
If I take the resultant ASCIIencoded string and put it into PlayerPrefs, it magically turns into an empty string when I fetch it back to from playerprefs. Very bizarre.
Thanks if anyone can point out what is wrong with these methods, possible incompatibilities with PlayerPrefs.SetString string encoding, or any community examples of what I’m trying to accomplish, it would be much appreciated! Thanks.
private string SerializeString (System.Object graphObj)
{
int count = 0;
byte[] byteArray;
char[] charArray;
ASCIIEncoding charEncoding = new ASCIIEncoding ();
MemoryStream stream = new MemoryStream (2048);
BinaryFormatter formatter = new BinaryFormatter ();
// serialize the object into a stream
formatter.Serialize (stream, graphObj);
stream.Seek (0, SeekOrigin.Begin);
// convert to byte array
byteArray = new byte[stream.Length];
while (count < stream.Length)
byteArray[count++] = Convert.ToByte (stream.ReadByte ());
// convert to char array
charArray = new char[charEncoding.GetCharCount (byteArray, 0, count)];
charEncoding.GetDecoder ().GetChars (byteArray, 0, count, charArray, 0);
return new string (charArray);
}
private System.Object DeserializeString (string serializedStr)
{
byte[] byteArray;
char[] charArray;
MemoryStream stream;
ASCIIEncoding charEncoding = new ASCIIEncoding ();
BinaryFormatter formatter = new BinaryFormatter ();
// convert string into char array
charArray = serializedStr.ToCharArray ();
// char array into byte array
byteArray = new byte[charEncoding.GetByteCount (charArray)];
charEncoding.GetEncoder ().GetBytes (charArray, 0, charArray.Length, byteArray, 0, false);
// convert byte array into memorystream
stream = new MemoryStream (byteArray);
stream.Seek (0, SeekOrigin.Begin);
// deserialize the memorystream into restored object
return formatter.Deserialize( stream );
}
Here is my class - maybe it will be useful for someone else? This does create dependency on System.Core.dll which is only 6KB however- a small price to pay for the convenience.
using UnityEngine;
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
public class PlayerPrefsSerializer
{
public static BinaryFormatter bf = new BinaryFormatter ();
// serializableObject is any struct or class marked with [Serializable]
public static void Save (string prefKey, object serializableObject)
{
MemoryStream memoryStream = new MemoryStream ();
bf.Serialize (memoryStream, serializableObject);
string tmp = System.Convert.ToBase64String (memoryStream.ToArray ());
PlayerPrefs.SetString ( prefKey, tmp);
}
public static object Load (string prefKey)
{
string tmp = PlayerPrefs.GetString (prefKey, string.Empty);
if (tmp == string.Empty )
return null;
MemoryStream memoryStream = new MemoryStream (System.Convert.FromBase64String (tmp));
return bf.Deserialize (memoryStream);
}
}
The best would be to always use protobuf-net, although it has some tricky points to make work under iOS, but anyway in the end of serialization you have to base64 encode the bytes to store it as string in PlayerPrefs
I realize this is really old but does it still work? I am trying to serialize my PlayerPrefs, but when I use this, I see no difference in how the string looks in Registry. Any help would be welcome!
I found the serialization feature of Unity3D’s JsonUtility to be too short sighted (e.g., it doesn’t serialize DateTime fields), and for my purpose Json.NET was overkill. I merged the examples in this thread together, modernized them a bit, and fixed some memory leaks:
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
/// <summary>
/// Store objects in PlayerPrefs
/// </summary>
static class Serializer {
/// <summary>
/// Serialize the <paramref name="item"/> and save it to PlayerPrefs under the <paramref name="key"/>.
/// <paramref name="item"/> class must have the [Serializable] attribute. Use the
/// [NonSerialized] attribute on fields you do not want serialized with the class.
/// </summary>
/// <param name="key">The key</param>
/// <param name="item">The object</param>
internal static void Save(string key, object item) {
using (var stream = new MemoryStream()) {
formatter.Serialize(stream, item);
var bytes = stream.ToArray();
var serialized = Convert.ToBase64String(bytes);
PlayerPrefs.SetString(key, serialized);
}
}
/// <summary>
/// Load the <paramref name="key"/> from PlayerPrefs and deserialize it.
/// </summary>
/// <param name="key">The key</param>
internal static T Load<T>(string key) {
if (!PlayerPrefs.HasKey(key)) return default(T);
var serialized = PlayerPrefs.GetString(key);
var bytes = Convert.FromBase64String(serialized);
T deserialized;
using (var stream = new MemoryStream(bytes)) {
deserialized = (T)formatter.Deserialize(stream);
}
return deserialized;
}
static readonly BinaryFormatter formatter = new BinaryFormatter();
}