2

I am trying to write data to a file using this code;

   public async Task<bool> WriteDataAsync<T>(T data, string fileName, string folderPath)
    {
        try
        {
            var store = await GetFolder(folderPath);

            using (MemoryStream sessionData = new MemoryStream())
            {
                DataContractSerializer serializer = new DataContractSerializer(typeof(T));
                serializer.WriteObject(sessionData, data);

                StorageFile sessionFile = await store.CreateFileAsync(fileName, CreationCollisionOption.ReplaceExisting);

                using (Stream fileStream = await sessionFile.OpenStreamForWriteAsync())
                {
                    sessionData.Seek(0, SeekOrigin.Begin);
                    await sessionData.CopyToAsync(fileStream);
                    await fileStream.FlushAsync();
                }
            }

        }

For a long time it worked OK, but then I got this exception;

Type 'Microsoft.Practices.Prism.PubSubEvents.EventAggregator' with data contract name 'EventAggregator:http://schemas.datacontract.org/2004/07/Microsoft.Practices.Prism.PubSubEvents' is not expected. Consider using a DataContractResolver if you are using DataContractSerializer or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to the serializer.

So I looked into the DataContractResolver and I found this page in MSDN;

https://msdn.microsoft.com/en-us/library/ee358759(v=vs.110).aspx

I created the code to fix my error;

public class LogResolver : DataContractResolver
{
    public override bool TryResolveType(Type dataContractType, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
    {
        if (dataContractType != typeof(LogViewModel))
        {
            return knownTypeResolver.TryResolveType(dataContractType, declaredType, null, out typeName,
                out typeNamespace);
        }

        var dictionary = new XmlDictionary();
        typeName = dictionary.Add("Log");
        typeNamespace = dictionary.Add("http://tempuri.com");
        return true;
    }

    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
    {
        if (typeName == "Log" && typeNamespace == "http://tempuri.com")
        {
            return typeof(LogViewModel);
        }

        return knownTypeResolver.ResolveName(typeName, typeNamespace, null, null);
    }
}  

And

DataContractSerializer serializer = new DataContractSerializer(typeof(T));
if (typeof(T).Name == "LogViewModel")
{
    var writer = XmlDictionaryWriter.CreateDictionaryWriter(XmlWriter.Create(sessionData));
    serializer.WriteObject(writer, data, new LogResolver());
}
else
{
    serializer.WriteObject(sessionData, data);
}

But now my code will not compile because the serializer.WriteObject method does not take 3 parameters. How can this be? Surely MSDN cannot be wrong?

EDIT I decided to look at the definition of the serialisation class where WriteObject resides and I found this;

// Decompiled with JetBrains decompiler
// Type: System.Runtime.Serialization.XmlObjectSerializer
// Assembly: System.Runtime.Serialization.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// MVID: 94FDD0A8-CB5D-4815-A53D-5DC4F6C5FA80
// Assembly location: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.6\Profile\Profile32\System.Runtime.Serialization.Xml.dll

using System.IO;
using System.Xml;

namespace System.Runtime.Serialization
{
  public abstract class XmlObjectSerializer
  {
    protected XmlObjectSerializer();
    public abstract bool IsStartObject(XmlDictionaryReader reader);
    public virtual bool IsStartObject(XmlReader reader);
    public virtual object ReadObject(Stream stream);
    public virtual object ReadObject(XmlDictionaryReader reader);
    public abstract object ReadObject(XmlDictionaryReader reader, bool verifyObjectName);
    public virtual object ReadObject(XmlReader reader);
    public virtual object ReadObject(XmlReader reader, bool verifyObjectName);
    public abstract void WriteEndObject(XmlDictionaryWriter writer);
    public virtual void WriteEndObject(XmlWriter writer);
    public virtual void WriteObject(Stream stream, object graph);
    public virtual void WriteObject(XmlDictionaryWriter writer, object graph);
    public virtual void WriteObject(XmlWriter writer, object graph);
    public abstract void WriteObjectContent(XmlDictionaryWriter writer, object graph);
    public virtual void WriteObjectContent(XmlWriter writer, object graph);
    public abstract void WriteStartObject(XmlDictionaryWriter writer, object graph);
    public virtual void WriteStartObject(XmlWriter writer, object graph);
  }
}

So there is not WriteObject method that uses DataContractResolver. So what shall I do?

EDIT - A workaround not a solution. If you look at the error message the problem was that the objects being written contained an event which could not be serialised. This event was defined in the BaseViewModel class used in all ViewModels. So I created a new base class only to be used by those ViewModels that need it, as follows; namespace M.Survey.UILogic.ViewModels { using M.Survey.UILogic.Helpers; using Microsoft.Practices.Prism.PubSubEvents;

public class BaseEventViewModel : BaseViewModel
{
    public BaseEventViewModel()
    {
        this.EventAgg = EventAggHolder.EventAgg;
    }

    public IEventAggregator EventAgg { get; set; }
}

So my problem is now fixed. But it leaves open how to write an object that contains an event. In WinRT I was unable to find an attribute that could exclude the event from serialisation that causes this bug.

arame3333
  • 9,887
  • 26
  • 122
  • 205

1 Answers1

1

I suspect that this WriteObject overload is not supported on WinRT. You could try following alternatives.

  1. Using DataContractSerializerSettings to set the DataContractResolver and then pass the settings object to the DataContractSerializer constructor. Then serializer.WriteObject(writer, data) can be used as usual.

    DataContractSerializerSettings settings = new DataContractSerializerSettings();
    settings.DataContractResolver = new LogResolver();
    DataContractSerializer serializer = new DataContractSerializer(typeof(T), settings);
    
  2. Using KnownType attibute to specify the list of known types to be detected by the serializer.

More info: https://social.msdn.microsoft.com/Forums/expression/en-US/9cea4fa4-7dde-46b9-9d51-40eca3d21a18/how-to-assing-knowntypeattribute-on-winrt?forum=winappswithcsharp

Hope this could help in your scenario!

Edit: As the problem is with the unneeded property is being serialized, using the IgnoreDataMember will instruct the serializer to exclude it from Serialization.

Community
  • 1
  • 1
Sivaram Koduri
  • 509
  • 1
  • 5
  • 12
  • The first solution did not work and the second solution was too complicated for me to figure out. I figured that the problem was that I had an event in the object (see error message) that it could not serialise. I was hoping to find an attribute that could prevent the event from serialising but I was unable to do so. So I found a way of creating a new base class from which only the classes that needed that event would use it. This will work as long as I do not need to write an object from a class that does. – arame3333 Mar 03 '17 at 11:41
  • If that's the case then can you please add the class definition so that if we can find a easy workaround to overcome the problem. – Sivaram Koduri Mar 03 '17 at 12:01
  • 1
    Did you try adding [IgnoreDataMember](https://msdn.microsoft.com/en-us/library/system.runtime.serialization.ignoredatamemberattribute.aspx) attribute on the event property which will instruct **DataContractSerializer** to exclude it from Serialization. – Sivaram Koduri Mar 03 '17 at 14:28
  • I have now, and it works! Thank you for that! The answer has changed now, so if you write it up I will tick it. – arame3333 Mar 03 '17 at 16:04
  • Good to hear that! I have update my answer with the solution. – Sivaram Koduri Mar 03 '17 at 16:09
  • *I have kept the other information as it would have been solved the problem without event fields in case of WinRT.* – Sivaram Koduri Mar 03 '17 at 16:19