I am trying to use BinaryFormatter to serialize game data.
I have serializable classes that I am able to serialize , write to a file, and deserialze without issue. However, when I serialize and write to the file, then stop the app and try to deserialize when running the next time, I get a file not found expception on the call BinaryFormatter:eserialize with the file name a string of (seemingly) random characters.
//Serialization Code
fs = new FileStream(Application.dataPath + “/Game.xml”, FileMode.Open);
desPro = new DESCryptoServiceProvider();
desPro.Key = ASCIIEncoding.ASCII.GetBytes(“passpass”);
desPro.IV = ASCIIEncoding.ASCII.GetBytes(“passpass”);
cs = new CryptoStream(fs, desPro.CreateEncryptor(), CryptoStreamMode.Write);
The assembly name generated by Unity is generally random. This screws up the BinaryFormatter so when the assembly changes (I think it is every compile), the prev saved data will not load. The random characters is this assembly name.
To fix this, you need to create and implement a DeserializationBinder to re-set the assembly and types.
Here is an example binder:
public sealed class VersionDeserializationBinder : SerializationBinder
{
public override Type BindToType( string assemblyName, string typeName )
{
if ( !string.IsNullOrEmpty( assemblyName ) !string.IsNullOrEmpty( typeName ) )
{
Type typeToDeserialize = null;
assemblyName = Assembly.GetExecutingAssembly().FullName;
// The following line of code returns the type.
typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) );
return typeToDeserialize;
}
return null;
}
}
You then set this in your BinaryFormater for deserialization:
formatter.Binder = new VersionDeserializationBinder();
And in which way would the change of the assembly impact the Serialization of an assembly independent XML file? (always assuming its ensured that it is not part of the build so not within assets, because in that case all these attempts will fail)
It’s because he/she is using the BinaryFormatter to save the actual XML data, and the BinaryFormatter chokes if the assembly changes between serialize and deserialize.
So that’s why I gave the binder reply. Hopefully that is the actual problem.
Revisited this thread recently as I’m on track to implement a “store / load scene state” capability for an iphone game and the Formatters are a must there.
If I end using the xml one in the end will have to be seen, I would prefer the binary one.
I’m trying to implement VersionDeserializationBinder but i’m geting this error:
Assets/GameStateCS.cs(2604,25): error CS0246: The type or namespace name `Type’ could not be found. Are you missing a using directive or an assembly reference?
you should be using System.Type unless you have using System; at the top (which is not that common in case of unity due to UnityEngine.Object vs System.Object
But I’m currently stumped by the implementation of the SerializationBinder.
I’ve got some code to save some data to a binary file, and reload that data… this all works well so long as I don’t change any code to cause Unity to recompile. Once it does recompile, I can no longer load from my file, it says I cannot load… this sounds like what the OP originally had going on. The solution that was proposed is the SerializationBinder. I can only assume that it’s my assembly that’s changing as well, so I attempted to implement the code provided earlier in this thread.
Sadly nothing changed… so I decided to dig a bit deeper and see where it was failing… so I tossed a Debug.Log() into the SerializationBinder… only to see that it didn’t get called. Does anyone know under what circumstances these Binders are called? I’m sure I’ve made a very simple mistake… but I can’t for the life of me see it.
object result;
Stream stream = File.Open(path, FileMode.Open);
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Binder = new VersionDeserializationBinder();
result = bFormatter.Deserialize(stream);
stream.Close();
return result;
SerializationBinder code:
public sealed class VersionDeserializationBinder : SerializationBinder
{
public VersionDeserializationBinder() : base() { }
public override Type BindToType(string assemblyName, string typeName)
{
if (!string.IsNullOrEmpty(assemblyName) !string.IsNullOrEmpty(typeName))
{
Type typeToDeserialize = null;
assemblyName = Assembly.GetExecutingAssembly().FullName;
// The following line of code returns the type.
typeToDeserialize = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));
return typeToDeserialize;
}
return null;
}
}
Has anyone run across this before? Why might my Binder not be called?
I have managed to find a slightly different method to overcome the downfall of randomized Binary names every code change. Instead of adding List's to the sInfo directly… instead you can serialize the count first, and then each element individually. It has something to do with Generic Lists being Strong-Typed…which leads to issues. Seems to be working well, but if anyone knows why my VersionBinder wasn’t calling… I’d still like to know.
Sorry to up this thread, but i’m having the same problem.
I’m doing a binary serialization of a dictionary, and everything goes well aslong tht you dont chenge the project, if i change any code, it recompiles and i get an error.
Anything i can do to solve this?
DavidB, did you have to save the list in separate parts and the put it together on loading it? How you did that?
Hey Gilson, just move the dictionary to 2 Arrays when serializing and then reconstruct the Dictionary when deserializing with your serialized arrays.
I was having the same problem with a List, and this just worked out for me!
checkout:
private List<Entity> Entities;
// Deserialization
public InteractiveSpace(SerializationInfo info, StreamingContext ctxt)
{
// We save the Entities list in an Array so the VersionDeserializationBinder works as expected if we change the code.
// Seems that Unity and Mono are not so friendly when serializing generic attributes.
Entity[] loadedEntitiesArr = (Entity[]) info.GetValue("EntitiesList", typeof(Entity[]));
Entities = new List<Entity>(loadedEntitiesArr);
}
// Serialization function
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
Entity[] savedArrEntities = Entities.ToArray();
info.AddValue("EntitiesList", savedArrEntities);
}