【问题标题】:Failing to read XML content using XmlSerializer使用 XmlSerializer 读取 XML 内容失败
【发布时间】:2017-05-19 20:18:22
【问题描述】:

我在课堂上得到了这个:

namespace MSAToolsLibrary.PublisherEntry
{
    [XmlRoot(ElementName = "PublisherDatabase", Namespace = "http://www.publictalksoftware.co.uk/msa")]
    public class PublisherData
    {
        public PublisherData()
        {
            //_Publishers = new List<Publisher>();
            _PublishersDictionary = new Dictionary<string, Publisher>();
        }

        public List<Publisher> Publishers
        {
            get { return _PublishersDictionary.Select(x => x.Value).ToList(); }
            set { _PublishersDictionary = value.ToDictionary(x => x.Name, x => x); }
        }
        private Dictionary<string, Publisher> _PublishersDictionary;

        [XmlIgnore]
        public Dictionary<string, Publisher> PublisherDictionary
        {
            get { return _PublishersDictionary; }
        }


        public void AddPublisher(String strName, String strNotes, Gender eGender, Appointed eAppointedAs, Serving eServingAs, bool bUseForDemonstrations, bool bAvailableMidweek, bool bAvailableWeekend, DateTime[] listDatesNotAvailable)
        {
            Publisher _Publisher = new Publisher()
            {
                Name = strName,
                Notes = strNotes,
                Gender = eGender,
                AppointedAs = eAppointedAs,
                ServingAs = eServingAs,
            };

            _Publisher.Assignments.UseForDemonstrations = bUseForDemonstrations;
            _Publisher.Availability.Midweek = bAvailableMidweek;
            _Publisher.Availability.Weekend = bAvailableWeekend;
            _Publisher.Availability.DatesNotAvailable = new List<DateTime>(listDatesNotAvailable);

            //_Publishers.Add(_Publisher);
            _PublishersDictionary.Add(strName, _Publisher);
        }
    }
}

现在,当我将数据保存到 XML 时,一切正常。

但是当我阅读时:

public void ReadPublisherData(out Int64 iResult)
{
    _PublisherData.Publishers.Clear(); // Reset

    iResult = MakeResult(true);

    try
    {
        XmlSerializer x = new XmlSerializer(_PublisherData.GetType());
        using (StreamReader reader = new StreamReader(_strPathXML))
        {
            _PublisherData = (PublisherData)x.Deserialize(reader);
            iResult = _PublisherData.PublisherDictionary.Count;
        }
    }
    catch
    {
        iResult = MakeResult(false);
    }
 }

不起作用。我在列表或字典中的发布者为零。

我做错了什么?

更新

如果我更改 PublisherData 声明以使其具有所需的后退字段:

public PublisherData()
{
    _Publishers = new List<Publisher>();
    _PublishersDictionary = new Dictionary<string, Publisher>();
}

public List<Publisher> Publishers
{
    get => _Publishers; set => _Publishers = value;
}
private List<Publisher> _Publishers;

然后这会导致数据正确序列化,我得到了 MFC 应用程序中的预期结果。但现在我的PublisherDictionary 挂了。于是我加了一个函数:

   public void BuildPublisherDictionary()
    {
        _PublishersDictionary = _Publishers.ToDictionary(x => x.Name, x => x);
    }

并调整了读取例程:

   public void ReadPublisherData(out Int64 iResult)
    {
        iResult = MakeResult(true);

        try
        {
            _PublisherData.Publishers.Clear(); // Reset

            XmlSerializer x = new XmlSerializer(_PublisherData.GetType());
            using (StreamReader reader = new StreamReader(_strPathXML))
            {
                _PublisherData = (PublisherData)x.Deserialize(reader);

                _PublisherData.BuildPublisherDictionary();
                iResult = _PublisherData.PublisherDictionary.Count;
            }
        }
        catch
        {
            iResult = MakeResult(false);
        }
     }

它有效。但我不知道这是否是最简单的方法。我对上面的问题是列表/字典现在彼此分离。因此,如果我将发布者添加到字典中,它现在不会出现在列表中。

更新 2

目前,在阅读 XML 之后,如果我添加或删除一个发布者,我会将它应用于列表和字典。除非有更简单的方法。

【问题讨论】:

  • 首先用记事本检查 XML 文件,看看文本是否好看。看起来序列化程序正在添加命名空间,但没有添加阅读器。
  • 您的 Publishers 属性取决于 Dictionary 但该 Dictionary 不会在反序列化时填充。为什么 Publishers 不能通过支持字段简单实现?
  • XML 文件很好,因为它是使用应用程序创建的。所以我知道它的结构是好的。
  • @rene 我不太明白。那它应该如何工作?我需要“Publishers”列表进行序列化,但我需要“PublishersDictionary”用于一般用途。我应该如何改变它?
  • @rene 按照我的理解,Publishers setter 将从值对象中填充字典。

标签: c# com xmlserializer


【解决方案1】:

序列化程序只会调用 List 的 getter,然后为它找到的每个元素调用 Add,并为其提供从该元素反序列化的类型的实例。

如果您的要求是同时拥有一个列表和一个字典,那么您必须提供一个类型的实现,该类型实现了这一点并实现了一个适用的接口。

以下 ListDict 使用 List 和 Dictionary 作为它们的后备存储,同时实现 IList 以用于(反)序列化。

public class ListDict<K, T>:IList<T>
{

   public Dictionary<K, T> dict = new Dictionary<K, T>();
   public List<T> list = new List<T>();
   Func<T,K> KeyFunc;

   // takes an Function that returns a key for T
   public ListDict(Func<T,K> keyfunc) 
   {
      KeyFunc = keyfunc;
   } 

   // called by the serializer
   public void Add(T value) 
   {
      Add( KeyFunc(value), value);
   }

   // fill both List and Dictionary
   public void Add(K key, T value) 
   {
      list.Add(value);
      dict.Add( key , value);
   }
   // left out other required methods from IList<T>
}

现在您的 PublisherData 类将进行如下更改以利用上述类:

[XmlRoot(ElementName = "PublisherDatabase", Namespace = "http://www.publictalksoftware.co.uk/msa")]
public class PublisherData
{
    private ListDict<string, Publisher> _PublishersDictionary;
    public PublisherData()
    {
        // provide the function to generate a key for a Publisher
        _PublishersDictionary = new ListDict<string,Publisher>( (p) => p.Name  );   
    }

    [XmlElement]
    public ListDict<string,Publisher> Publishers
    { 
        get { return _PublishersDictionary; }
    }


    [XmlIgnore]
    public Dictionary<string, Publisher> PublisherDictionary
    {
        get {return _PublishersDictionary.dict; }
    }
}

使用上面的类在反序列化后直接给我一个填充列表和字典。当然,您必须确保在 ListDict 中保持后备存储同步。也许您可以不使用它,但这取决于您的确切用例。

【讨论】:

  • 谢谢。我试过这个但得到一个编译错误:1>C:\Program Files (x86)\Microsoft Visual Studio\2017rc\Community\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.targets(4470,5): warning : Type库导出器警告处理“MSAToolsLibrary.PublisherEntry.ListDict`2,MSAToolsLibrary”。警告:类型库导出器遇到泛型类型。泛型类可能不会暴露给 COM。
  • 您在 COM 场景中使用该类型?或者您是否正在为此编译以防万一
  • 我正在构建一个从 MFC 使用的 C# DLL。
  • 哇,好的。你最好再问一个新问题,然后用 MFC 和 COM 标记它,这样像 Hans Passant 这样的人可能会偶然发现它。这两个世界之间的编组超出了我目前的知识范围。我必须深入研究以了解发生了什么。
  • 你有很多这些组合的列表/字典吗?因为如果只有几个,那么您可以简单地构建没有泛型的具体类。我介绍了泛型,因为您的示例代码已经使用了它。
猜你喜欢
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
  • 2021-05-03
  • 1970-01-01
  • 2011-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多