【问题标题】:C# use DataContractResolver to write data to a fileC# 使用 DataContractResolver 将数据写入文件
【发布时间】:2017-03-03 09:33:36
【问题描述】:

我正在尝试使用此代码将数据写入文件;

   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();
                }
            }

        }

很长一段时间它工作正常,但后来我得到了这个异常;

不应使用数据合同名称“EventAggregator:http://schemas.datacontract.org/2004/07/Microsoft.Practices.Prism.PubSubEvents”键入“Microsoft.Practices.Prism.PubSubEvents.EventAggregator”。如果您使用 DataContractSerializer 或将任何静态未知的类型添加到已知类型列表中,请考虑使用 DataContractResolver - 例如,通过使用 KnownTypeAttribute 属性或将它们添加到传递给序列化程序的已知类型列表中。

所以我查看了 DataContractResolver 并在 MSDN 中找到了这个页面;

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

我创建了代码来修复我的错误;

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);
    }
}  

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);
}

但是现在我的代码将无法编译,因为 serializer.WriteObject 方法不采用 3 个参数。 怎么会这样? MSDN 肯定不会错吧?

编辑 我决定看一下 WriteObject 所在的序列化类的定义,我发现了这个;

// 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);
  }
}

所以没有使用 DataContractResolver 的 WriteObject 方法。那我该怎么办?

编辑 - 解决方法不是解决方案。 如果您查看错误消息,问题是正在写入的对象包含无法序列化的事件。 此事件在所有 ViewModel 中使用的 BaseViewModel 类中定义。所以我创建了一个新的基类,仅供需要它的 ViewModel 使用,如下所示; 命名空间 M.Survey.UILogic.ViewModels { 使用 M.Survey.UILogic.Helpers; 使用 Microsoft.Practices.Prism.PubSubEvents;

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

    public IEventAggregator EventAgg { get; set; }
}

所以我的问题现在解决了。但它没有解决如何编写包含事件的对象。在 WinRT 中,我无法找到可以将导致此错误的事件从序列化中排除的属性。

【问题讨论】:

  • WriteObject 过载,需要 3 个参数,至少在 .NET 4.5 中
  • @arame3333 你的项目目标框架是什么?正如我所见,此错误仅出现在 .NET Framework
  • 这是一个在 WinRT 项目中使用的类库。目标框架是portable-win81+wpa81
  • 感谢您的意见。我刚刚用一些关键信息编辑了这个问题。

标签: c# prism


【解决方案1】:

我怀疑 WinRT 不支持此 WriteObject 重载。您可以尝试以下替代方案。

  1. 使用 DataContractSerializerSettings 设置 DataContractResolver,然后将设置对象传递给 DataContractSerializer 构造函数。那么serializer.WriteObject(writer, data)就可以照常使用了。

    DataContractSerializerSettings settings = new DataContractSerializerSettings();
    settings.DataContractResolver = new LogResolver();
    DataContractSerializer serializer = new DataContractSerializer(typeof(T), settings);
    
  2. 使用KnownType 属性指定序列化程序要检测的已知类型列表。

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

希望这对您的方案有所帮助!

编辑:由于问题在于不需要的属性被序列化,使用IgnoreDataMember 将指示序列化程序将其从序列化中排除。

【讨论】:

  • 第一个解决方案不起作用,第二个解决方案太复杂了,我无法弄清楚。我认为问题在于我在对象中有一个无法序列化的事件(请参阅错误消息)。我希望找到一个可以阻止事件序列化的属性,但我无法这样做。所以我找到了一种创建新基类的方法,只有需要该事件的类才能使用它。只要我不需要从一个类中编写一个对象,这将起作用。
  • 如果是这种情况,那么请您添加类定义,以便我们找到解决问题的简单解决方法。
  • 您是否尝试在事件属性上添加 IgnoreDataMember 属性,该属性将指示 DataContractSerializer 将其从序列化中排除。
  • 我现在有了,它有效!谢谢你!现在答案已经改变,所以如果你写出来我会打勾。
  • 很高兴听到这个消息!我已经用解决方案更新了我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-02
  • 2013-09-16
  • 1970-01-01
相关资源
最近更新 更多