I submitted an update for my WP7 app and I've had users complain that their data was erased after the update. I am storing my data by serializing my ViewModel class as an XML file in IsolatedStorage. During my testing I noticed that updating the app caused the settings file to get partially overwritten with a serialized copy of a new instance (with default values) of my ViewModel object. I thought I had solved the problem by using FileMode.Create when I write to my XML file, but I guess not.
Could the serialization have gone wrong because I added new properties to the ViewModel object and deserializing from the existing XML file failed? I do have my code set to instantiate a new ViewModel object if one cannot be read from the XML file. If this is the case, does it mean I cannot add any new properties to my ViewModel object?
Edit:
Here is the structure of my ViewModel, not really all that complex:
public class MyClass: INotifyPropertyChanged
{
public MyClass()
{
// Set defaults
this.Items= new ObservableCollection<Item>();
this.TextTemplate = "default";
this.HasSeenSomething = false;
}
public ObservableCollection<Item> Items { get; set; }
// New properties added in app update
public string TextTemplate { get; set; }
public bool HasSeenSomething { get; set; }
}
Here is the code I'm using to serialize/deserialize my ViewModel. I think it is pretty standard but maybe I've botched something:
public static void WriteToXml<T>(T data, string path)
{
var xmlWriterSettings = new XmlWriterSettings { Indent = true };
using (var myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (var stream = myIsolatedStorage.OpenFile(path, FileMode.Create))
{
var serializer = new XmlSerializer(typeof(T));
using (var xmlWriter = XmlWriter.Create(stream, xmlWriterSettings))
{
serializer.Serialize(xmlWriter, data);
}
}
}
}
public static T ReadFromXml<T>(string path)
{
T data = default(T);
try
{
using (var myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(path))
{
using (var stream = myIsolatedStorage.OpenFile(path, FileMode.Open))
{
try
{
var serializer = new XmlSerializer(typeof(T));
object instance = serializer.Deserialize(stream);
if (instance != null) data = (T)instance;
}
catch (System.Exception ex)
{
var e = ex;
}
}
}
}
}
catch(System.Exception ex)
{
var e = ex;
}
return data;
}
You can, but there are a few gotchas.
You need to remember that the ctor does not get called when you deserialize your object. That means that if you do, in the ctor, the following:
public MyClass() { MyV2Collection = new Collection(); }
And you have no other mechanism that will new up the collection if it's null, you will die on NullRef the first time you try to access the property.
Dime to dollars, I'd bet you are hitting pt #2.
Fixing it is fairly easy, but you need to remember to do it. The options are:
public
) that has the [OnDeserialized]
attribute decorating it. That code will run when deserialization is done and you can instantiate your member there.