开发者

Deserialization problem: Error when deserializing from a different program version

开发者 https://www.devze.com 2023-03-13 06:24 出处:网络
I finally decided myself to post my problem, after a couple of hours spent searching the Internet for solutions and trying some.

I finally decided myself to post my problem, after a couple of hours spent searching the Internet for solutions and trying some.

[Problem Context]

I am developing an application which will be deployed in two parts:

  • an XML Importer tool: its role is to Load/Read an xml file in order to fill some datastructures, which are afterwards serialized into a binary file.
  • the end user application: it will Load the binary file generated by the XML Importer and do some stuff with the recovered data structures.

For now, I only use the XML Importer for both purposes (meaning I first load the xml and save it to a binary file, then I reopen the XML Importer and load my binary file).

[Actual Problem]

This works just fine and I am able to recover all the data I had after XML loading, as long as I do that with the same build of my XML Importer. This is not viable, as I will need at the very least two different builds, one for the XML Importer and one for the end user application. Please note that the two versions of the XML Importer I use for my testing are exactly the same concerning the source code and thus the datastructures, the only difference lies in the build number (to force a different build I just add a space somewhere and build again).

So what I'm trying to do is:

  • Build a version of my XML Importer
  • Open the XML Importer, load an XML fil开发者_Python百科e and save the resulting datastructures to a binary file
  • Rebuild the XML Importer
  • Open the XML Importer newly built, load the previously created binary file and recover my datastructures.

At this time, I get an Exception:

SerializationException: Could not find type 'System.Collections.Generic.List`1[[Grid, 74b7fa2fcc11e47f8bc966e9110610a6, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]'.
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadType (System.IO.BinaryReader reader, TypeTag code)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadTypeMetadata (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo)
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)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObject (BinaryElement element, System.IO.BinaryReader reader, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info)

For your information (don't know if useful or not), the actual type it is struggling to deserialize is a List, Grid being a custom Class (which is correctly serializable, as I am able to do it when using the same version of XML Importer).

[Potential Solution]

I do believe it comes from somewhere around the Assembly, as I read many posts and articles about this. However, I already have a custom Binder taking care of the differences of Assembly names, looking like this:

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; 
    }
}

which I assign to the BinaryFormatter before deserializing here:

    public static SaveData Load (string filePath) 
    {
        SaveData data = null;//new SaveData ();
        Stream stream;

        stream = File.Open(filePath, FileMode.Open);


        BinaryFormatter bformatter = new BinaryFormatter();
        bformatter.Binder = new VersionDeserializationBinder(); 
        data = (SaveData)bformatter.Deserialize(stream);
        stream.Close();

        Debug.Log("Binary version loaded from " + filePath);

        return data; 
    }

Do any of you guys have an idea on how I could fix it? Would be awesome, pretty please :)


Move the working bits to a separate assembly and use the assembly in both "server" and "client". Based on your explanation of the problem, this should get around the "wrong version" problem, if that is the core issue. I would also take any "models" (i.e. bits of state like Grid) to a domain model project, and use that in both places.


I just bumped into your thread while I had the same problem. Especially your code sample with the SerializationBinder helped me a lot. I just had to modify it slightly to tell a difference between my own assemblies and those of Microsoft. Hopefully it still helps you, too:

sealed class VersionDeserializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        Type typeToDeserialize = null;
        string currentAssemblyInfo = Assembly.GetExecutingAssembly().FullName;

        //my modification
        string currentAssemblyName = currentAssemblyInfo.Split(',')[0];
        if (assemblyName.StartsWith(currentAssemblyName))assemblyName = currentAssemblyInfo;

        typeToDeserialize = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName));
        return typeToDeserialize;
    }
}


I believe the problem is that you are telling it to look for List<> in the executing assembly, whereas in fact it is in the System assembly. You should only re-assign the assembly name in your binder if the original assembly is one of yours.

Also, you might have to handle the parameter types for generics specifically in the binder, by parsing out the type name and making sure the parameter types are not specific to the foreign assembly when you return the parameterized generic type.

0

精彩评论

暂无评论...
验证码 换一张
取 消