Iris Classon
Iris Classon - In Love with Code

Example Metro app /WinRT: Serializing and deseralizing objects to StorageFile and LocalFolder using generics and async/await threading

EDIT- Please have a look at a newer blog post with a more detailed example, that uses XMLserializer (both ways are included int the code). A few errors have also been fixed Example Metro app /WinRT: Serializing and deseralizing objects using XMLSerializer to StorageFile and LocalFolder using generics and async/await threading

This one was a hard one! I just couldn’t find ANY working examples of how to serialize and deserialize objects to local storage or local file. And it should be so simple, right? Well, here is an example app as always - enjoy :)

Serializing and deseralizing objects to local storage/ local file using generics and async/await threading

The sample app can be downloaded here

Problem:
Your app has to work offline as well and you are looking for a way to store smaller amount of data. You would like to serialize objects to a the local storage in a local file.

Some requriements applicable from Application Profile Survey:
Networking: behaves well offline and while on intermittent internet connection…
Performance: rely on local data as much as possible…

The serialize-helperclass
[sourcecode language=“csharp”]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
using System.IO;
using System.Runtime.Serialization;
using Windows.Storage.Streams;

namespace SerializeListWinRT.DataModel
{
class LocalStorage
{
private static List _data = new List();

    public static List<object> Data  
    {  
        get { return \_data; }  
        set { \_data = value; }  
    }  

    private const string filename = "cats.xml";  

    static async public Task Save<T>()  
    {  
        await Windows.System.Threading.ThreadPool.RunAsync((sender) => LocalStorage.SaveAsync<T>().Wait(), Windows.System.Threading.WorkItemPriority.Normal);  
    }  

    static async public Task Restore<T>()  
    {  
            await Windows.System.Threading.ThreadPool.RunAsync((sender) => LocalStorage.RestoreAsync<T>().Wait(), Windows.System.Threading.WorkItemPriority.Normal);  
    }  

    static async private Task SaveAsync<T>()  
    {  
        StorageFile sessionFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);  
        IRandomAccessStream sessionRandomAccess = await sessionFile.OpenAsync(FileAccessMode.ReadWrite);  
        IOutputStream sessionOutputStream = sessionRandomAccess.GetOutputStreamAt(0);  
        var sessionSerializer = new DataContractSerializer(typeof(List<object>), new Type[] { typeof(T) });  
        sessionSerializer.WriteObject(sessionOutputStream.AsStreamForWrite(), \_data);  
        await sessionOutputStream.FlushAsync();  
    }  

    static async private Task RestoreAsync<T>()  
    {  
        StorageFile sessionFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists);  
        if (sessionFile == null)  
        {  
            return;  
        }  
        IInputStream sessionInputStream = await sessionFile.OpenReadAsync();  
        var sessionSerializer = new DataContractSerializer(typeof(List<object>), new Type[] { typeof(T) });  
        \_data = (List<object>)sessionSerializer.ReadObject(sessionInputStream.AsStreamForRead());  
    }  
}  

}
[/sourcecode]

The class that will be serialized
[sourcecode language=“csharp”]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace SerializeListWinRT
{
[KnownType(typeof(SerializeListWinRT.Cat))]
[DataContractAttribute]
public class Cat
{
[DataMember()]
public String Name { get; set; }

    [DataMember()]  
    public String About { get; set; }  
}  

}
[/sourcecode]
The view
[sourcecode language=“XML”]

namespace SerializeListWinRT
{

public sealed partial class MainPage : Page  
{  
    private readonly ObservableCollection<Cat> \_cats = new ObservableCollection<Cat>();  

    public ObservableCollection<Cat> Cats  
    {  
        get { return \_cats; }  
    }  

    public MainPage()  
    {  
        this.InitializeComponent();  
        ClearLists();  
        AddCatsToList();  
        CreateNewCat();  
    }  

    private async void AddCatsToList()  
    {  
        await LocalStorage.Restore<Cat>();  
        SetCatList();  
    }  

    private void ClearLists()  
    {  
        Cats.Clear();  
        LocalStorage.Data.Clear();  
    }  

    private void SetCatList()  
    {  
        foreach (var item in LocalStorage.Data)  
        {  
            \_cats.Add(item as Cat);  
        }  
    }  

    public Cat NewCat { get; set; }  

    private void CreateNewCat()  
    {  
        NewCat = new Cat();  
    }  

    private void AddNewCat()  
    {  
        \_cats.Add(new Cat {Name = NewCat.Name, About = NewCat.About});  
    }  
    protected override void OnNavigatedTo(NavigationEventArgs e)  
    {  
    }  

    private void Button\_Click\_1(object sender, RoutedEventArgs e)  
    {  
        AddNewCat();  
    }  

    private void Button\_Click\_2(object sender, RoutedEventArgs e)  
    {  
        AddNewCat();  
        SaveList();  
    }  

    private void SaveList()  
    {  
        LocalStorage.Data.Add(NewCat);  
        LocalStorage.Save<Cat>();  
    }  

}  

}
[/sourcecode]

Comments

Leave a comment below, or by email.
Lisa
7/10/2012 7:38:51 PM
Thanks! 
Mike
10/18/2012 4:30:28 PM
I get the following exception thrown when the call in your code to Save() or Restore() is made. Any idea why?

System.AggregateException was unhandled by user code
  HResult=-2146233088
  Message=One or more errors occurred.
  Source=mscorlib
  StackTrace:
       at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
       at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
       at System.Threading.Tasks.Task.Wait()
       at SerializeListWinRT.DataModel.LocalStorage.b__4[T](IAsyncAction sender) in c:\temp\store\SerializeListWinRT_small\SerializeListWinRT\LocalStorage.cs:line 33
  InnerException: System.Xml.XmlException
       HResult=-2146232000
       Message=Unexpected end of file.
       Source=System.Runtime.Serialization
       LineNumber=0
       LinePosition=0
       StackTrace:
            at System.Xml.EncodingStreamWrapper.ReadBOMEncoding(Boolean notOutOfBand)
            at System.Xml.EncodingStreamWrapper..ctor(Stream stream, Encoding encoding)
            at System.Xml.XmlUTF8TextReader.SetInput(Stream stream, Encoding encoding, XmlDictionaryReaderQuotas quotas, OnXmlDictionaryReaderClose onClose)
            at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(Stream stream)
            at SerializeListWinRT.DataModel.LocalStorage.d__11`1.MoveNext() in c:\temp\store\SerializeListWinRT_small\SerializeListWinRT\LocalStorage.cs:line 55
       InnerException: 
Mike
10/18/2012 5:20:03 PM
Reply to: Mike
Never mind my last comment, I figured this out. Thanks. 
Puhek
11/19/2012 3:39:38 AM
I appreciate the code but I guess there is an issue with the RestoreAsync method; I moved this to the roaming storage, but I guess it does not matter;
You are using CreateFileAsync method to locate the file (open it). This will create a file if there is none and return null to the object serializer which will fail.

I have used GetFileAsync that throws when file is not found but does not create an empty one. You need to catch the exception though.

        public static async Task ReadRemotelyAsync()
        {
            try
            {
                var sessionFile = await ApplicationData.Current.RoamingFolder.GetFileAsync(AppSettingsFilename);
                var sessionInputStream = await sessionFile.OpenReadAsync();
                var sessionSerializer = new DataContractSerializer(typeof(T));
                return (T)sessionSerializer.ReadObject(sessionInputStream.AsStreamForRead());
            }
            catch (Exception)
            {
                return default(T);
            }
        } 
syamala
2/6/2013 1:38:16 AM
The above code is giving the following warning .Can u pls give solution for this.

"Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call."

private void SaveList()
        {
            LocalStorage.Data.Add(NewCat);
            LocalStorage.Save();  //warning here
        } 
Daniel Schroeder
6/11/2013 5:03:21 PM
I posted a related solution on Stack Overflow that you may be interested in checking out (http://stackoverflow.com/questions/12562268/xml-serialization-in-windows-8/17055641#17055641) 
Iris Classon
6/14/2013 5:52:34 AM
Reply to: Daniel Schroeder
Thank you, appreciate that! 
sNO
7/14/2013 2:31:58 PM
Reply to: Mike
Hi, I'm having the same problem. How did you solve it? Thanks in advance! 


Last modified on 2012-07-09

comments powered by Disqus