【问题标题】:XmlSerializer, GenerateSerializer and CollectionsXmlSerializer、GenerateSerializer 和集合
【发布时间】:2013-02-07 04:13:54
【问题描述】:

我们的项目中有一些设置类,它们使用来自一些 .config 文件的 XmlSerializer 进行序列化/反序列化。在其中一些设置类中,我们有如下子设置的集合:

using System;
using System.Collections.ObjectModel;
using System.Xml.Serialization;

namespace Datev.Framework.Shared.Actions.Setup
{
    [Serializable]
    [XmlSerializerAssembly]
    [XmlRoot("setup")]
    public class SetupXml
    {
        public SetupXml()
        {
            SubSetups = new Collection<SubSetupXml>();
        }

        [XmlArray("subSetups")]
        [XmlArrayItem("subSetup")]
        public Collection<SubSetupXml> SubSetups { get; private set; }
    }

    [Serializable]
    public class SubSetupXml
    {
        [XmlElement("someValue")]
        public string SomeValue { get; set; }
    }
}

我们使用属性 [XmlSerializerAssembly] 来获得读取和写入设置的最佳性能。 这是我的问题:我们使用 Collection 来避免 CA 警告“不要使用数组”。当我们公开 SubSetups 的设置器时,我们会收到 CA-Warning CA2227“不要公开集合的设置器”。如果我们将属性 SubSetups 的设置器设为私有(就像在代码示例中一样),我们将在生成的序列化程序中得到一个错误。方法“GenerateSerializer”(在我们的一个工具中调用)代码有这样一行:

if (o.SubSetups == null) o.SubSetups = new Collection<SubSetupXml>();

如果我们将 setter 设为私有,我们将在构建序列化程序期间收到 CS0200“无法分配属性子设置”。 有谁知道如何在不抑制 CA 警告的情况下使用生成的序列化程序进行正确设置?

【问题讨论】:

  • 我认为你不应该盲目地遵循 CA 警告。在某些情况下,使用引起警告的代码是最好的选择,当然比尝试解决它、试错风格要好。

标签: c# xml-serialization


【解决方案1】:

很难说:“正确”的设置很大程度上取决于上下文。 一个快速的想法:如果将逻辑从“GenerateSerializer”移动到属性 getter 会发生什么?可以接受吗?

[XmlArray("subSetups")]
[XmlArrayItem("subSetup")]
public Collection<SubSetupXml> SubSetups { 
   get {
      // subSetups needs to be a backing (private) field... is this a problem?
      if (this.subSetups == null) this.subSetups = new Collection<SubSetupXml>();
   }
   private set; 
}

这样,在“GenerateSerializer”中你就得到了集合。如果该集合尚未创建,它将位于 getter 内部,而无需在类外部创建它。 只是一个想法,如果不适用请告诉我。

【讨论】:

  • 不幸的是,它不适用于 XmlSerializer 类的 GenerateSerializer 方法...
【解决方案2】:

如果情况像看起来那样简单,那么您不需要检查是否(o.SubSetups == null),因为您在SetupXml() 构造函数中有SubSetups = new Collection&lt;SubSetupXml&gt;(); 行(当然,如果o 是类型为SetupXml)。如果您从GenerateSerializer 方法中删除if 语句并将setter 设为私有您应该没问题-SubSetups 属性不可能是null,除非有其他一些方法可以解决它你没有提到...

【讨论】:

  • 感谢您的回复,但问题是我们无法更改 GenerateSerializer 方法,因为它在 Microsoft 的 XmlSerializer 类中...
  • @Fischermaen 我明白了。我试图追溯您的步骤,但没有成功,您能否发布您如何调用GenerateSerializer 方法?
【解决方案3】:

你可以有这样的东西:

    public class SetupXml
    {
        public SetupXml()
        {
            SubSetups = new Collection<SubSetupXml>();
        }

        [XmlIgnore]
        public Collection<SubSetupXml> SubSetups { get; private set; }

        [EditorBrowsable(EditorBrowsableState.Never)]
        [GeneratedCodeAttribute("Whatever", "1.0.0.0")]
        [XmlArray("subSetups")]
        [XmlArrayItem("subSetup")]          
        public SubSetupXml[] SerializationSubSetups
        {
            get
            {
                return SubSetups.ToArray();
            }
            get
            {
                SubSetups = new SubSetups();
                if (value != null)
                {
                    foreach(SubSetupXml ssx in value)
                    {
                        SubSetups.Add(ssx);
                    }
                }
            }
        }
    }

它并不完美,但EditorBrowsable 属性会阻止使用此库(来自另一个程序集)的开发人员看到它由智能感知/自动完成工具显示。并且 GeneratedCode 属性将阻止 CA 对其发出警告。

【讨论】:

  • 感谢您的想法 - 但这在我看来就像抑制 CA 警告一样。我们之前使用了数组并抑制了 CA 警告。然后我想开发一种没有 CA(并压制它们)的“好”方法,但我找不到方法。我们的问题是,它可以使用 ICollection 类型的属性,但是在使用类 XmlSerializer 的方法“GenerateSerializer”生成 XmlSerializer 时出现错误。出于性能方面的考虑,我们正在这样做。
【解决方案4】:

我假设您已经查阅过这篇文章? http://msdn.microsoft.com/en-us/library/ms182327.aspx

这让我印象深刻,这是那篇文章中的一个重要说明: “二进制和 XML 序列化都支持作为集合的只读属性。System.Xml.Serialization.XmlSerializer 类对实现 ICollection 和 System.Collections.IEnumerable 以便可序列化的类型有特定要求。”

除此之外,您还可以使用 .Clear() 和 .AddRange() 方法取得更多进展,详见此处。

【讨论】:

  • 我已经阅读了这篇文章。我们的问题是,由于性能方面的原因,我们使用属性 [XmlSerializerAssembly] 并使用 XmlSerializer 类的 GenerateSerializer() 方法生成该属性。如果我们按照您提到的文章将 setter 设为私有,我们会在调用 GenerateSerializer 时出错。
【解决方案5】:

你可以尝试实现 IXmlSerializable 接口。它需要更多的工作,但它可以让您很好地控制序列化,并在您的情况下访问私有类变量。在 ReadXml 方法中,您只需创建集合的实例,遍历源 xml 中的每个节点并解析值

【讨论】:

    【解决方案6】:

    将 SubSetups 的类型更改为 IEnumerable 将摆脱代码分析警告,但我不知道这是否适合您。

        [XmlArray("subSetups")]
        [XmlArrayItem("subSetup")]
        public IEnumerable<SubSetupXml> SubSetups { get; set; }
    

    【讨论】:

    • 如果我正确理解了这个问题,如果你将设置器设为private,则生成的代码将不起作用。但是,如果您将其设为public,则会收到另一个 CA 警告。
    • 哎呀,我的意思是让二传手公开。我没有收到 IEnumerable 的 CA 警告。
    • @svick:你的评论是对的。不幸的是,IEnumerable 版本不适用于 GenerateSerializer ... :-(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多