何时使用 ShouldSerializeXXX 与 XmlIgnoreAttribute 进行 XML 序列化

When to use ShouldSerializeXXX vs. XmlIgnoreAttribute for XML serialization

我一直在寻找在 xml 序列化和反序列化期间忽略 class 的 属性 的示例。我找到了三种不同的方法,但不知道什么时候应该使用它们。我特别感兴趣的是,哪个更适合 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; }
    }
    

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

#1 和#2 之间的基本区别是它们在选项 #3 中生成不同的 XML Schemas. If you want a member to be excluded from your type's schema, use [XmlIgnore]. If you want a member to be included conditionally, use ShouldSerializeXXX() or XXXSpecified. (Finally, as stated in this answer, [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() 的源代码以证明它总是 returns false。因此 Name 将出现在版本 #2 的模式中,尽管实际上从未出现过。如果您随后创建一个 Web 服务并使用 WSDL 发布您的模式(或者只是手动使它们可用),将为这两种类型生成不同的客户端——一种没有 Name 成员,一种有.

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

的版本
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 值的成员,但对于不可为 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 Support and

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

  • [XmlIgnore] 无法在派生的 class 中被覆盖,但 ShouldSerializeXXX() 可以在标记为虚拟时被覆盖;有关示例,请参阅 here

  • 如果一个成员不能被XmlSerializer序列化,因为,例如,它指的是一个lacks a parameterless constructor的类型,那么用[XmlIgnore]标记该成员将允许包含类型被序列化——添加 ShouldSerializeXXX() { return false; } 将不允许包含类型被序列化,因为如前所述 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.NET and protobuf-net.

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