Search Unity

Reading data with DataContract gives SerializationException when loading older version

Discussion in 'Scripting' started by quyrean, Jun 24, 2018.

  1. quyrean

    quyrean

    Joined:
    Nov 29, 2016
    Posts:
    16
    I am trying to use DataContract to save and load my runtime user data, however it does not appear to be working in Unity as specified by the Microsoft documentation

    https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-versioning

    According to the docs, I should be able to change the name of a member and map it using DataMember attribute. However with Unity I get a serialization error when trying to load an older save file.

       System.Runtime.Serialization.SerializationException : Field "Phone" not found in class Person


    Am I doing something wrong or is this a known issue with Unity. I am using unity 2017.1.0f3 on Windows 10.

    thanks!

    Here are the steps to reproduce:
    1. create class

    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Runtime.Serialization;
    4.  
    5. [System.Serializable]
    6. [DataContract]
    7. public class Person
    8. {
    9.    [DataMember]
    10.    public string Phone;
    11. }
    12.  

    2. save data to disk with
    BinaryFormatter.Serialize

    3. load data from disk with
    BinaryFormatter.Deserialize
    - no errors
    4. modify class - change "Phone" to "Telephone"

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Runtime.Serialization;
    3.  
    4. [System.Serializable]
    5. [DataContract]
    6. public class Person
    7. {
    8.    //rename Phone to Telephone
    9.    [DataMember(Name = "Phone")]
    10.    public string Telephone;
    11. }
    12.  
    5. load data from disk with
    BinaryFormatter.Deserialize
    - get error

      System.Runtime.Serialization.SerializationException : Field "Phone" not found in class Person
    ---
    at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadTypeMetadata (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo) [0x00216] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:691
    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) [0x0000a] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:269
    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) [0x0009d] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:195
    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) [0x00105] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:223
    at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadNextObject (BinaryElement element, System.IO.BinaryReader reader) [0x00020] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:130
    at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectGraph (BinaryElement elem, System.IO.BinaryReader reader, Boolean readHeaders, System.Object& result, System.Runtime.Remoting.Messaging.Header[]& headers) [0x00004] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/ObjectReader.cs:104
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.NoCheckDeserialize (System.IO.Stream serializationStream, System.Runtime.Remoting.Messaging.HeaderHandler handler) [0x00078] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/BinaryFormatter.cs:179
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize (System.IO.Stream serializationStream) [0x00000] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Runtime.Serialization.Formatters.Binary/BinaryFormatter.cs:136
    at Persist.Load (System.String path) [0x0003c] in C:\Users\quyrean\Documents\unity projects\tutorial-heart-ui-test\Assets\contract test\Persist.cs:33
    at PersonTest+PersistTest.test_1_load () [0x00007] in C:\Users\quyrean\Documents\unity projects\tutorial-heart-ui-test\Assets\Editor\PersonTest.cs:22
    at (wrapper managed-to-native) System.Reflection.MonoMethod:InternalInvoke (object,object[],System.Exception&)
    at System.Reflection.MonoMethod.Invoke (System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x000d0] in /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Reflection/MonoMethod.cs:222
    ---

    Reading file c:/tmp/fred.bin

    fyi: code to save and load

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Runtime.Serialization;
    3. using System.Runtime.Serialization.Formatters.Binary;
    4. using System.IO;
    5.  
    6. public static class Persist {
    7.  
    8.    //save and restore character data for pc's
    9.    //default path C:\Users\quyrean\AppData\LocalLow\BohnStar\NTrek1\info1.bin
    10.    public static void Save(string path, Person data) {
    11.        Debug.Log ("Saving to " + path);
    12.        //binary
    13.        IFormatter formatter = new BinaryFormatter();
    14.        Stream stream2 = new FileStream(path + ".bin",
    15.            FileMode.Create,
    16.            FileAccess.Write, FileShare.None);
    17.        formatter.Serialize(stream2, data);
    18.        stream2.Close();
    19.    }
    20.  
    21.    public static Person Load(string path)
    22.    {
    23.        path += ".bin";
    24.        Person answer;
    25.  
    26.        if (File.Exists (path)) {
    27.            Debug.Log ("Reading file " + path);
    28.            IFormatter formatter = new BinaryFormatter ();
    29.            Stream stream = new FileStream (path,
    30.                FileMode.Open,
    31.                FileAccess.Read,
    32.                FileShare.Read);
    33.            answer = (Person)formatter.Deserialize (stream);
    34.            stream.Close ();
    35.            return answer;
    36.  
    37.        } else {
    38.            Debug.Log ("File does not exist " + path);
    39.        }
    40.  
    41.        return null;
    42.    }
    43. }
    44.  
     
  2. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    Well you are trying to read a file that has the phone value and map it on an object with that value missing because of renaming to telephone. You need to load it, save it as a new value, delete the phone value and seralize it again afaik.
     
  3. quyrean

    quyrean

    Joined:
    Nov 29, 2016
    Posts:
    16
    Yes, that is what I am trying to do. That is why I used the attribute:

    [DataMember(Name = "Phone")]


    According to the microsoft docs, this should be backwards compatible.

    https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-versioning