Unity BinaryFormatter Deserialization Problem

Unity is having an issue selecting the right object for deserialization.

I have successfully serialized to a file and then deserialized from that file in binary format.

But when I try to deserialize from a URL, I’m getting a deserialization error.

The attached project includes a Unity project and a Mono project.

Both versions attempt to deserialize a Mono binary serialized file and a Unity serialized file.

With the same code, MonoDevelop is able to execute the Mono project and get the expected result.

Unfortunately, the Unity editor is getting a deserialization error.

You will find the MonoProject solution in Deserialization002/MonoProject

The expected output running from Mono should be:

From File: Deserialized okay
From Unity Url: Failed to deserialize
From Mono Url: Deserialized okay

The expected output running from the Unity editor should be:

From File: Deserialized okay
From Unity Url: Deserialized okay
From Mono Url: Failed to deserialize

The expected output running from the Unity player should be:

From File: Failed to deserialize
From Unity Url: Deserialized okay
From Mono Url: Failed to deserialize

This is the Unity serialized url:
http://tagenigma.com/qa/Unity3d/WIP/Maps/TF_CityMap_Unity.txt

This is the Mono serialized url:
http://tagenigma.com/qa/Unity3d/WIP/Maps/TF_CityMap_Mono.txt

Here are the rrror details:

System.IO.FileNotFoundException: Could not load file or assembly '8b8fe8916a42f423e841d8c0e08ffe85' or one of its dependencies. The system cannot find the file specified.
File name: '8b8fe8916a42f423e841d8c0e08ffe85'
  at <0x00000> <unknown method>
  at (wrapper managed-to-native) System.AppDomain:LoadAssembly (string,System.Security.Policy.Evidence,bool)
  at System.AppDomain.Load (System.String assemblyString) [0x00000] 
  at (wrapper remoting-invoke-with-check) System.AppDomain:Load (string)
  at System.Reflection.Assembly.Load (System.String assemblyString) [0x00000] 
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetDeserializationType (Int64 assemblyId, System.String className) [0x00000] 
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadType (System.IO.BinaryReader reader, TypeTag code) [0x00000] 
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadTypeMetadata (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo) [0x00000] 
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectInstance (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo, System.Int64 objectId, System.Object value, System.Runtime.Serialization.SerializationInfo info) [0x00000] 
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObject (BinaryElement element, System.IO.BinaryReader reader, System.Int64 objectId, System.Object value, System.Runtime.Serialization.SerializationInfo info) [0x00000] 
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObject (BinaryElement element, System.IO.BinaryReader reader, System.Int64 objectId, System.Object value, System.Runtime.Serialization.SerializationInfo info) [0x00000] 
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (System.IO.BinaryReader reader) [0x00000] 
  at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (System.IO.BinaryReader reader, Boolean readHeaders, System.Object result, System.Runtime.Remoting.Messaging.Header[] headers) [0x00000] 
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) [0x00000] 
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) [0x00000] 
  at SerializeCache.DeserializeUrl (System.String url, System.Type type) [0x0002a] in /Users/user/Unity/Deserialization002/Assets/SerializeCache.cs:138 
UnityEngine.Debug:LogError(Object)

111320–4267–$deserialization002_152.zip (619 KB)

I tried using the WWW.bytes to deserialize but I get the same error.

I’m doing the following:

	// Use this for initialization
	IEnumerator Start ()
	{
		UnityEngine.WWW www =
			new WWW(_TerrainFragmentMapUnityUrl);
		yield return www;
		
		BinaryFormatter bf =
			new BinaryFormatter();
		
		using (MemoryStream ms =
		       new MemoryStream([url]www.bytes[/url], 0, [url]www.bytes.Length[/url]))
		{
			_TerrainFragmentList =
				(List<TerrainFragment>)bf.Deserialize(ms);
		}
		
		_GuiText.text = AttemptSerialization();		
		UnityEngine.Debug.Log(_GuiText.text);
	}

And the editor gives me the following error.

FileNotFoundException: Could not load file or assembly '8b8fe8916a42f423e841d8c0e08ffe85' or one of its dependencies. The system cannot find the file specified.
System.AppDomain.Load (System.String assemblyString) 
(wrapper remoting-invoke-with-check) System.AppDomain:Load (string)
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) 
TAGTerrainScript+<>c__CompilerGenerated0.MoveNext ()   (at Assets/TAGTerrainScript.cs:35)

111384–4272–$deserialization003_129.zip (619 KB)

This is basically how I do it (exact code untested):

private WWW webStream;
private bool downloading;

public void Start
{
	webStream = new WWW( "http://my.file.name" );
	downloading = true;
}

public void Update()
{
	if( downloading  webStream.isDone )
	{
		downloading = false;
		HandleData( webStream.bytes );
	}
}

public void HandleData( byte[] data )
{
	MySpecialClass myObject;
	MemoryStream memoryStream;
	BinaryFormatter binaryFormatter;
	
	memoryStream = new MemoryStream( data );
	binaryFormatter = new BinaryFormatter();
	
	myObject = binaryFormatter.Deserialize( memoryStream ) as MySpecialClass;
	
	// Handle //
}

Ok to wrap this up from the IRC discussion. The error comes from unity producing a differently named assembly on each build which causes problems with serialisation of contained classes.

A workaround is to define all data classes for serialisation in external assemblies.

This should be bug reported though.

The core issue here is that the Unity compiled assembly name changes (looks like a guid). And therefore, the assembly cannot be found for deserialization.

The painful workaround is to put the data structures into external DLL assets.

I moved the deserialization structures to an external assembly and added the asset. Unfortunately, I get the same error.

I’ll make another attempt to serialize with the external class to see if that works.

111455–4273–$deserialization004_955.zip (628 KB)

The binary signature has to match for an object to be capable of being deserialized.

Now since both the Mono project and the Unity project use the external assembly to store the data structures, the serialized objects can be exchanged.

This example works in both the Unity Editor and the Unity Player.

The serialized object can be generated in Unity or the Mono project and the player will be capable of deserializing it.

111459–4274–$deserialization005_134.zip (629 KB)

The WWW class appears to be required. With HttpWebRequest deserialization is working on MonoDevelop and Unity Editor, but not the Unity Player.

Looks like I’m fighting security here.

(Filename: /Users/unity-build/Desktop/automatic-build-2/unity/Projects/../Runtime/Export/Generated/BaseClass.cpp Line: 1651)

System.NotSupportedException: [url]http://tagenigma.com/qa/unity3d/wip/maps/tf_citymap_external.txt[/url]
  at System.Net.WebRequest.GetCreator (System.String prefix) [0x00000] 
  at System.Net.WebRequest.Create (System.Uri requestUri) [0x00000] 
  at System.Net.WebRequest.Create (System.String requestUriString) [0x00000] 
  at SerializeCache.DeserializeUrl (System.String url, System.Type type) [0x00000] 
UnityEngine.Debug:LogError(Object)
SerializeCache:smile:eserializeUrl(String, Type)
TAGTerrainScript:AttemptSerialization()
TAGTerrainScript:Start()
 
(Filename: /Users/unity-build/Desktop/automatic-build-2/unity/Projects/../Runtime/Export/Generated/BaseClass.cpp Line: 1651)

From Binary Url: Failed to deserialize

UnityEngine.Debug:Log(Object)
TAGTerrainScript:Start()
 
(Filename: /Users/unity-build/Desktop/automatic-build-2/unity/Projects/../Runtime/Export/Generated/BaseClass.cpp Line: 1651)

111472–4275–$deserialization006_689.zip (629 KB)

Was this ever reported as a bug? I’m running into this same issue in Unity 3 so it’s clearly not been fixed.

Necro

This issue was solved by using a serializationbinder long ago.

Necro or not, this is still an issue.

I appreciate the link to the SerializationBinder docs, but I don’t find them terribly enlightening… does anyone have a simple example of how you would use this to make serialization work in the web player?