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

时间:2017-07-19 11:42:11

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

我在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; }
    }
    
  4. 如果对stackoverflowmsdnXmlIgnoreAttribtueNonSerializedAttribute之间的区别有一些解释,我无法找到有关何时使用{{1}的信息当XmlIgnoreAttribtue模式时。我使用XmlSerializer尝试了它们,并且它们都看到了预期的工作。

1 个答案:

答案 0 :(得分:3)

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

要查看#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。首先,具有无条件值属性的版本:

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>

架构#2与预期相符,但请注意#1和#3之间存在差异:第一个有minOccurs="1"而第三个有minOccurs="0"。产生差异是因为XmlSerializer documented默认情况下跳过具有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]标记该成员将允许包含类型要序列化 ​​- 添加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.NETprotobuf-net

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