一尘不染

ShouldSerialize *()与*指定的条件序列化模式

c#

我知道ShouldSerialize 模式和 Specified模式以及它们的工作方式,但是两者之间有什么区别吗?

当某些事情应该有条件地序列化时,是否存在使用一种方法相对于另一种方法的“陷阱”?

此问题特定于的用法XmlSerializer,但也欢迎提供有关此主题的一般信息。

关于此主题的信息很少,这可能是因为它们执行的目的完全相同,这是一种样式选择。但是,.NET实现者会通过反射分析类并查找任何一种/两种模式来确定所生成的序列化器的行为,这似乎很奇怪,因为除非它只是一个向后兼容的构件,否则它会减慢序列化器的生成。

编辑: 对于那些不熟悉这两种模式的人,如果该*Specified属性或ShouldSerialize*方法返回true,则该属性被序列化。

public string MyProperty { get; set; }

//*Specified Pattern
[XmlIgnore]
public bool MyPropertySpecified { get{ return !string.IsNullOrWhiteSpace(this.MyProperty); } }

//ShouldSerialize* Pattern
public bool ShouldSerializeMyProperty()
{
     return !string.IsNullOrWhiteSpace(this.MyProperty);
}

阅读 611

收藏
2020-05-19

共1个答案

一尘不染

XML模式绑定支持中{propertyName}Specified记录了该模式的意图:MinOccurs属性绑定支持。添加它是为了支持XSD架构元素,其中:

  • <element>元素是参与。
  • minOccurs 为零。
  • maxOccurs的 属性使然一个实例。
  • 数据类型转换为值类型。

在这种情况下,xsd.exe /classes将自动生成(或您可以手动生成)与架构元素同名的属性,以及一个{propertyName}Specified布尔型的get
/ set属性 ,该 属性 跟踪该元素是否在XML中遇到,并应序列化回XML。
如果遇到元素,{propertyName}Specified则设置为true,否则设置为false。因此,反序列化的实例可以确定属性是否在原始XML中未设置(而不是显式设置为默认值)。

逆也用于模式生成。如果您定义具有一对与上述模式匹配的属性的C#类型,然后用于xsd.exe生成相应的XSD文件,minOccurrs则会将适当的XSD文件添加到架构中。例如,给定以下类型:

public class ExampleClass
{
    [XmlElement]
    public decimal Something { get; set; }

    [XmlIgnore]
    public bool SomethingSpecified { get; set; }
}

将生成以下架构,反之亦然:

<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="ExampleClass" nillable="true" type="ExampleClass" />
  <xs:complexType name="ExampleClass">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" name="Something" type="xs:decimal" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>

请注意,虽然xsd.exe只记录了{propertyName}Specified为值类型属性自动生成属性的情况,XmlSerializer但是当手动将其用于引用类型属性时,将遵循该模式。

您可能会问,为什么在这种情况下xsd.exe不绑定到Nullable<T>?可能是因为:

您需要了解这种模式,因为xsd.exe有时会自动为您生成该模式,但是属性与其Specified属性之间的交互很奇怪,并且容易产生错误。您可以填充类中的所有属性,然后序列化为XML并丢失
所有内容,
因为您也没有将相应的Specified属性设置为true。这里有时会出现这个“陷阱”,例如,也可以参见这个问题这个问题

这种模式的另一个“陷阱”是,如果您需要使用不支持该模式的序列化程序来序列化类型,则 可能 要在序列化过程中手动抑制此属性的输出,并且 可能
需要在反序列化过程中手动进行设置。由于每个序列化程序可能都有自己的自定义机制来抑制属性(或者根本没有机制!),随着时间的推移,这样做会变得越来越繁重。

(最后,对于MyPropertySpecified没有设置器的情况下您的工作成功,我感到有些惊讶。我似乎想起了.Net
2.0的一个版本,在该版本中,缺少{propertyName}Specified设置器会导致抛出异常。但是在更高版本中,它不再可复制,我没有要测试的2.0。所以这可能是第三个陷阱。

Windows窗体控件的“属性”中ShouldSerialize{PropertyName}()记录了对该方法的支持:使用ShouldSerialize和Reset方法定义默认值。如您所见,文档位于MSDN的Windows窗体部分而不是该XmlSerializer部分,因此实际上是半隐藏功能。我不知道为什么支持此方法和Specified属性都存在于中XmlSerializer
ShouldSerialize是在.Net 1.1中引入的,我 相信 Min.ccurs绑定支持是在.Net
2.0
中添加的,所以也许早期的功能不能完全满足xsd.exe开发团队的需求(或品味)?

因为它是方法而不是属性,所以它缺少模式的“陷阱”
{propertyName}Specified。它在实践中似乎也更流行,并且已被其他序列化器采用,包括:

那么,使用哪种模式?

  1. 如果自动为您xsd.exe生成{propertyName}Specified属性,或者您的类型需要跟踪XML文件中是否出现了特定元素,或者您需要自动生成的XSD来指示某个值是可选的,请使用此模式并当心“陷阱”。

  2. 否则,请使用该ShouldSerialize{PropertyName}()模式。它的陷阱更少,并且可能得到更广泛的支持。

2020-05-19