Deserializing a newer version of an object from an older version of the object
Asked Answered
M

2

7

Suppose I had this class:

[Serializable]
public class SomeClass 
{
   public SomeClass() {//init}
   public string SomeString {get;set;}
}

This class gets Serialized when the application closes, and gets deserialized on the next run.

Then, I built it and released the application, and now the class has changed:

[Serializable]
public class SomeClass
{
   public SomeClass() {//init}
   public string SomeString {get;set;}
   public int SomeInt {get;set;}
}

Is there a way to set a property to its default on deserialization in case its not found in the old serialized object?

One way I thought about is keeping the old version of the class, then check the version that was serialized then looping properties of the old object and setting them in the new object, but this is non sense to me, any other solution that makes sense?

Mehalick answered 31/10, 2013 at 23:20 Comment(3)
What serializer? There are interfaces/attributes you can use to modify how the object [de]serializes but it's dependant on which library.Fonda
@BradChristie i use the BinaryFormatterMehalick
Have a look at using a custom binderFonda
A
5

You can mark fields with the attribute

[OptionalField()]

as explained in Version Tolerant Serialization

The class would then look like this:

[Serializable()]
public class SomeClass
{
    public SomeClass() {//init}
    public string SomeString { get; set; }

    [OptionalField(VersionAdded = 2)]
    public int SomeInt { get; set; }


    [OnDeserialized()]
    private void SetValuesOnDeserialized(StreamingContext context)
    {
        this.SomeInt = 10;
    }
}
Afflictive answered 31/10, 2013 at 23:39 Comment(6)
but is there no way to specify the default value , without [OnDeserialized()] , right? i tried by setting the backing field value but it gave 0 rather than 10 , but worked fine with [OnDeserialized()]Mehalick
As far as I know there is just one other way. You could implement the ISerializable interface which enforces you to implement the method GetObjectData (which is used in the serialization process) and you also need to implement an additional constructor with two parameters (SerializationInfo info, StreamingContext context) which will be used during the deserialization process. There you could also set the value of your field/property but in my opinion it's rather complicated.Afflictive
Isn't SetValuesOnDeserialized always called? Therefore, always overriding this.SomeInt? Therefore, it is always the same value after deserialization, no matter what the value is upon serialization?Writeup
@MikedeKlerk Actually yes it will but it is called before the object is populated with the values from the stream. So only if there is a value for the field in the stream then the default of 10 will be overwritten.Afflictive
@MarkusSafar Great, thanks! That clarifies it for me. Maybe I should have used OnDeserialized then instead of making backing fields for > 10 properties :D.Writeup
I would suggest using OnDeserializing rather than OnDeserialized. OnDeserializing is called before the deserialized values are set while OnDeserialized is called after -- which, if the optional field was present, would overwrite its deserialized value.Missionary
P
2

What i would do is base the SomeInt on a field where the field has a default value. IE.

public class SomeClass
{
    public SomeClass() { }

    int someInt = 10;

    public string SomeString { get; set; }
    public int SomeInt
    {
        get { return someInt; }
        set { someInt = value; }
    }
}

Then when the serializer deserializes the object if the SomeInt value is not provided the default value is still set.

EDIT: Update

Added a sample app using the XML serializer. Now to toggle the class type simply uncomment the #define serialize statement in row 2.

//uncomment for serialization
//#define serialize

using System;
using System.IO;
using System.Xml.Serialization;

namespace TestSerializer
{
    class Program
    {
        static void Main(string[] args)
        {

#if serialize

            SomeClass some = new SomeClass();
            some.SomeString = "abc";

            XmlSerializer serializer = new XmlSerializer(some.GetType());
            using (StringWriter writer = new StringWriter())
            {
                serializer.Serialize(writer, some);
                File.WriteAllText("D:\\test.xml", writer.ToString());
            }
#else
            XmlSerializer serializer = new XmlSerializer(typeof(SomeClass));
            using (StringReader reader = new StringReader(File.ReadAllText("D:\\test.xml")))
            {
                var o = serializer.Deserialize(reader) as SomeClass;
                if (o != null)
                    Console.WriteLine(o.SomeInt);
            }
            Console.ReadKey();
#endif
        }
    }



#if serialize

    [Serializable]
    public class SomeClass
    {
        public SomeClass() { }
        public string SomeString { get; set; }
    }
#else

    [Serializable]
    public class SomeClass
    {
        public SomeClass() { }
        private int someInt = 10;


        public string SomeString { get; set; }
        public int SomeInt
        {
            get { return someInt; }
            set { someInt = value; }
        }
    }
#endif
}
Pitiable answered 31/10, 2013 at 23:22 Comment(6)
let me test, i didnt think its as simple as thatMehalick
it did deserialize it but someInt value was 0 rather than 10!Mehalick
i tried that with multiple types, and what i get after deserializing is the default value of the type rather than the one specified in the fieldMehalick
I've just added a code example that should work for your case, you need to use the attribute OnDeserialized() for what you want.Afflictive
My appologies, i did not see that you were using a BinaryFormatter. This will not work for a binary formatter.Pitiable
Yes it does.. .. I have added a test sample using XmlSerializerPitiable

© 2022 - 2024 — McMap. All rights reserved.