【问题标题】:When to use ShouldSerializeXXX vs. XmlIgnoreAttribute for XML serialization何时使用 ShouldSerializeXXX 与 XmlIgnoreAttribute 进行 XML 序列化
【发布时间】:2017-12-24 15:53:37
【问题描述】:

我寻找了在 xml 序列化和反序列化期间忽略类属性的示例。我找到了三种不同的方法,但不知道什么时候应该使用它们。我特别感兴趣的是,哪个更适合XmlSerializer

  1. XmlIgnore属性

    public class Item
    {
        [XmlIgnore]
        public string Name { get; set; }
    }
    
  2. ShouldSerialize...开头的方法

    public class Item
    {
        public string Name { get; set; }
    
        public bool ShouldSerializeName()
        {
            return false;
        }
    }
    
  3. NonSerialized属性

    public class Item
    {
        [NonSerialized]
        public string Name { get; set; }
    }
    

stackoverflowmsdn 上有一些关于XmlIgnoreAttribtueNonSerializedAttribute 之间区别的解释,我无法找到有关何时使用XmlIgnoreAttribtue 以及何时使用ShouldSerializeXXX 模式的信息。我用 XmlSerializer 尝试了它们,它们都按预期工作。

【问题讨论】:

  • 如果它们都符合您的标准,请选择您喜欢的。 PS ShouldSerializeXXX 版本是一个糟糕的主意,不要使用那个!
  • @DavidG:请您辩解为什么使用ShouldSerializeXXX 是一个糟糕的主意。
  • 那么你会用所有这些额外的方法污染你的模型,而属性不会。
  • 另一个不使用 ShouldSerializeXXX 的原因是在实现了所有这些之后,当后来有人进来并需要更改字段名称时会发生什么。该任务现在在开发人员不知道的情况下破坏了序列化。
  • XmlSerializer 似乎没有像 JSON.net 这样的 ContractResolver,这使得 ShouldSerialize 成为动态包含/排除成员的唯一方法。我们使用 T4 模板来生成 ShouldSerializeXxxx 函数。我们有这么多属性,生成的文件是 10,000 行代码。

标签: c# c#-4.0 serialization xml-serialization xmlserializer


【解决方案1】:

#1 和#2 的基本区别在于它们生成不同的XML Schemas。如果您希望将某个成员从您的类型架构中排除,请使用[XmlIgnore]。如果您希望有条件地包含成员,请使用ShouldSerializeXXX()XXXSpecified。 (最后,如this answer 所述,选项#3 中的[NonSerialized]XmlSerializer 忽略。)

要查看#1 和#2 之间的区别,您可以使用xsd.exe 为您的类型生成模式。以下架构是为版本 #1 生成的,并且完全省略了 Name 成员:

<xs:complexType name="Item" />

虽然 #2 的以下有条件地包括 Name 成员:

<xs:sequence>
  <xs:element minOccurs="0" maxOccurs="1" name="Name" type="xs:string" />
</xs:sequence>

之所以出现差异是因为XmlSerializerxsd.exe 都执行静态类型分析而不是动态代码分析。这两种工具都无法确定在#2 中的Name 属性将始终被跳过,因为这两种工具都不会尝试反编译ShouldSerializeName() 的源代码以证明它始终返回false。因此Name 将出现在版本#2 的模式中,尽管实际上从未出现过。如果您随后创建一个 Web 服务并使用 WSDL 发布您的架构(或者只是手动使它们可用),将为这两种类型生成不同的客户端——一种没有Name 成员,另一种有。

当所讨论的属性是不可为空的值类型时,可能会产生额外的复杂性。考虑以下三个版本的Item。首先,一个无条件包含 value 属性的版本:

public class Item
{
    public int Id { get; set; }
}

生成以下架构,Id 始终存在:

  <xs:complexType name="Item">
    <xs:sequence>
      <xs:element minOccurs="1" maxOccurs="1" name="Id" type="xs:int" />
    </xs:sequence>
  </xs:complexType>

其次,具有无条件排除值属性的版本:

public class Item
{
    [XmlIgnore]
    public int Id { get; set; }
}

生成以下完全省略 Id 属性的架构:

  <xs:complexType name="Item" />

最后是一个带有条件排除值属性的版本:

public class Item
{
    public int Id { get; set; }

    public bool ShouldSerializeId()
    {
        return false;
    }
}

生成以下架构,Id 仅在条件下存在:

  <xs:complexType name="Item">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Id" type="xs:int" />
    </xs:sequence>
  </xs:complexType>

Schema #2 符合预期,但请注意 #1 和 #3 之间存在差异:第一个具有 minOccurs="1" 而第三个具有 minOccurs="0"。之所以出现差异,是因为 XmlSerializerdocumented 默认跳过具有 null 值的成员,但对于不可为空的值成员没有类似的逻辑。因此,情况#1 中的Id 属性将始终被序列化,因此架构中指示minOccurs="1"。只有启用条件序列化时才会生成minOccurs="0"。如果第三个模式又用于客户端代码生成,则会将IdSpecified 属性添加到自动生成的代码中,以跟踪在反序列化过程中是否实际遇到了Id 属性:

public partial class Item {

    private int idField;

    private bool idFieldSpecified;

    /// <remarks/>
    public int Id {
        get {
            return this.idField;
        }
        set {
            this.idField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool IdSpecified {
        get {
            return this.idFieldSpecified;
        }
        set {
            this.idFieldSpecified = value;
        }
    }
}

有关绑定到条件序列化值成员的更多详细信息,请参阅XML Schema Binding Support: MinOccurs Attribute Binding SupportShouldSerialize*() vs *Specified Conditional Serialization Pattern

这是主要区别,但也有次要区别可能会影响您的选择:

  • [XmlIgnore] 不能在派生类中被覆盖,但ShouldSerializeXXX() 在标记为虚拟时可以;示例见here

  • 如果一个成员不能被XmlSerializer 序列化,例如,它引用了lacks a parameterless constructor 的类型,那么用[XmlIgnore] 标记该成员将允许序列化包含类型 - 同时添加一个@987654377 @ 将不允许包含类型被序列化,因为如前所述 XmlSerializer 只执行静态类型分析。例如。以下:

    public class RootObject
    {
        // This member will prevent RootObject from being serialized by XmlSerializer despite the fact that the ShouldSerialize method always returns false.
        // To make RootObject serialize successfully, [XmlIgnore] must be added.
        public NoDefaultConstructor NoDefaultConstructor { get; set; }
    
        public bool ShouldSerializeNoDefaultConstructor() { return false; }
    }
    
    public class NoDefaultConstructor
    {
        public string Name { get; set; }
        public NoDefaultConstructor(string name) { this.Name = name; }
    }
    

    不能被XmlSerializer序列化。

  • [XmlIgnore] 特定于XmlSerializer,但ShouldSerializeXXX() 被其他序列化程序使用,包括Json.NETprotobuf-net

  • 如 cmets 中所述,在 Visual Studio 中重命名条件序列化属性不会自动重命名相应的 ShouldSerializeXXX() 方法名称,从而导致潜在的维护问题。

【讨论】:

  • 非常感谢您的详细解释。这给了我一个完美的决策指导。太好了。
猜你喜欢
  • 2011-01-21
  • 2010-12-06
  • 2012-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多