Search Unity

  1. Unity 2017.2 beta is now available for download.
    Dismiss Notice
  2. Unity 2017.1 is now released.
    Dismiss Notice
  3. Introducing the Unity Essentials Packs! Find out more.
    Dismiss Notice
  4. Check out all the fixes for 5.6 on the patch releases page.
    Dismiss Notice
  5. Help us improve the editor usability and artist workflows. Join our discussion to provide your feedback.
    Dismiss Notice

BinaryFormatter not supporting Version Tolerance?

Discussion in 'Scripting' started by shawnblais, Feb 11, 2015.

  1. shawnblais

    shawnblais

    Joined:
    Oct 11, 2012
    Posts:
    134
    According the the .NET docs, Version Tolerance was added to BinaryFormatter in .NET 2.0:https://msdn.microsoft.com/en-us/magazine/cc163902.aspx#S10

    So in theory, I should be able to remove a field from a Serialized class, and not have things blow up. ie:
    Code (CSharp):
    1.  
    2. //Version 1
    3. SaveData {
    4.     public int Num1;
    5.    public int Num2;
    6. }
    7.  
    8. //Version 2
    9. SaveData {
    10.    public int Num1;
    11. }
    Even though I removed Num2, the older versions of the class should still deserialize ok, due to Version Tolerance: "In the case of removing an unused member variable, the binary formatter will simply ignore the additional information found in the stream. " https://msdn.microsoft.com/en-us/magazine/cc163902.aspx#S10

    Instead, Unity is throwing an Error: System.Runtime.Serialization.SerializationException: Field "Num2" not found in class SaveData

    Is there a way to get this working in Unity?
     
    LiamMcGinley likes this.
  2. BillyMFT

    BillyMFT

    Joined:
    Mar 14, 2013
    Posts:
    142
    following
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    4,696
    Since unity uses Mono, it's that which needs to be considered when it comes to specific support like this.

    And decompiling System.Runtime.Serialization.Formmater.Binary.ObjectReader shows that it doesn't support this:

    Code (csharp):
    1.  
    2.     private ObjectReader.TypeMetadata ReadTypeMetadata(BinaryReader reader, bool isRuntimeObject, bool hasTypeInfo)
    3.     {
    4.       ObjectReader.TypeMetadata typeMetadata = new ObjectReader.TypeMetadata();
    5.       string str1 = reader.ReadString();
    6.       int length1 = reader.ReadInt32();
    7.       Type[] typeArray = new Type[length1];
    8.       string[] strArray = new string[length1];
    9.       for (int index = 0; index < length1; ++index)
    10.         strArray[index] = reader.ReadString();
    11.       if (hasTypeInfo)
    12.       {
    13.         TypeTag[] typeTagArray = new TypeTag[length1];
    14.         for (int index = 0; index < length1; ++index)
    15.           typeTagArray[index] = (TypeTag) reader.ReadByte();
    16.         for (int index = 0; index < length1; ++index)
    17.           typeArray[index] = this.ReadType(reader, typeTagArray[index]);
    18.       }
    19.       if (!isRuntimeObject)
    20.       {
    21.         long assemblyId = (long) reader.ReadUInt32();
    22.         typeMetadata.Type = this.GetDeserializationType(assemblyId, str1);
    23.       }
    24.       else
    25.         typeMetadata.Type = Type.GetType(str1, true);
    26.       typeMetadata.MemberTypes = typeArray;
    27.       typeMetadata.MemberNames = strArray;
    28.       typeMetadata.FieldCount = strArray.Length;
    29.       if (this._surrogateSelector != null)
    30.       {
    31.         ISurrogateSelector selector;
    32.         ISerializationSurrogate surrogate = this._surrogateSelector.GetSurrogate(typeMetadata.Type, this._context, out selector);
    33.         typeMetadata.NeedsSerializationInfo = surrogate != null;
    34.       }
    35.       if (!typeMetadata.NeedsSerializationInfo)
    36.       {
    37.         if (!typeMetadata.Type.IsSerializable)
    38.           throw new SerializationException("Serializable objects must be marked with the Serializable attribute");
    39.         typeMetadata.NeedsSerializationInfo = typeof (ISerializable).IsAssignableFrom(typeMetadata.Type);
    40.         if (!typeMetadata.NeedsSerializationInfo)
    41.         {
    42.           typeMetadata.MemberInfos = new MemberInfo[length1];
    43.           for (int index = 0; index < length1; ++index)
    44.           {
    45.             FieldInfo fieldInfo = (FieldInfo) null;
    46.             string name1 = strArray[index];
    47.             int length2 = name1.IndexOf('+');
    48.             if (length2 != -1)
    49.             {
    50.               string str2 = strArray[index].Substring(0, length2);
    51.               string name2 = strArray[index].Substring(length2 + 1);
    52.               for (Type baseType = typeMetadata.Type.BaseType; baseType != null; baseType = baseType.BaseType)
    53.               {
    54.                 if (baseType.Name == str2)
    55.                 {
    56.                   fieldInfo = baseType.GetField(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    57.                   break;
    58.                 }
    59.               }
    60.             }
    61.             else
    62.               fieldInfo = typeMetadata.Type.GetField(name1, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
    63.             if (fieldInfo == null)
    64.               throw new SerializationException("Field \"" + strArray[index] + "\" not found in class " + typeMetadata.Type.FullName);
    65.             typeMetadata.MemberInfos[index] = (MemberInfo) fieldInfo;
    66.             if (!hasTypeInfo)
    67.               typeArray[index] = fieldInfo.FieldType;
    68.           }
    69.           typeMetadata.MemberNames = (string[]) null;
    70.         }
    71.       }
    72.       if (!this._typeMetadataCache.ContainsKey((object) typeMetadata.Type))
    73.         this._typeMetadataCache[(object) typeMetadata.Type] = (object) typeMetadata;
    74.       return typeMetadata;
    75.     }
    76.  
    You could get around this by writing your own ISerializationSurrogate:
    https://msdn.microsoft.com/en-us/li...zation.iserializationsurrogate(v=vs.110).aspx

    This is what I did to get binary serialization support with unity object support in my serialization engine I've been working on.