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

JNY*_*ger 19 .net c# serialization xml-serialization xmlserializer

我知道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);
}
Run Code Online (Sandbox Code Playgroud)

dbc*_*dbc 32

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

  • <element>元素涉及.
  • minOccurs为零.
  • maxOccurs的属性决定了一个实例.
  • 数据类型转换为值类型.

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

对于模式生成也实现了逆.如果使用与上述模式匹配的一对属性定义C#类型,则使用xsd.exe生成相应的XSD文件,将相应的适当minOccurrs添加到模式中.例如,给定以下类型:

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

    [XmlIgnore]
    public bool SomethingSpecified { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

<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>
Run Code Online (Sandbox Code Playgroud)

请注意,虽然xsd.exe仅记录为自动生成{propertyName}Specified值类型属性的属性,XmlSerializer但在手动使用参考类型属性时将遵循该模式.

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

您需要了解这种模式,因为xsd.exe有时会自动为您生成它,但是属性与其Specified属性之间的交互很奇怪并且容易产生错误.您可以填写类中的所有属性,然后序列化为XML并丢失所有内容,因为您还没有设置相应的Specified属性true.这个"问题"不时出现在这里,例如看到这个问题或者这个问题.

使用此模式的另一个"问题"是,如果您需要使用不支持此模式的序列化程序序列化您的类型,您可能希望在序列化期间手动抑制此属性的输出,并且可能需要在反序列化期间手动设置它.由于每个序列化程序可能都有自己的自定义机制来抑制属性(或根本没有机制!),这样做会随着时间的推移变得越来越繁重.

(最后,我有点惊讶你的MyPropertySpecified工作没有setter成功.我似乎记得.Net 2.0的一个版本,其中一个丢失的{propertyName}Specifiedsetter会导致异常被抛出.但它在以后的版本中不再可重现,并且我没有2.0测试.所以这可能是第三个问题.)

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

因为它是一种方法而不是属性,它缺乏模式的"陷阱" {propertyName}Specified.它似乎在实践中更受欢迎,并已被其他序列化器采用,包括:

那么,使用哪种模式?

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

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


Tib*_*ibi 6

为了添加@dbc的非常详细的答案,我在管理派生类中的序列化时遇到了一个问题。在我的情况下,我有一个基类和一个派生类,其中的Prop属性被覆盖。

public class BaseClass
{
    public virtual string Prop {get; set;}
}

public class Derived: BaseClass
{
    public string Comp1 {get; set;}
    public string Comp2 {get; set;}
    public override string Prop {get => Comp1 + Comp2; set {}}
}
Run Code Online (Sandbox Code Playgroud)

由于Prop在派生类属性进行计算,对于Derived类,我想序列化Comp1Comp2,但不会Prop。事实证明,在类中的XmlIgnore属性上设置属性不起作用,并且无论如何都要序列化。PropDerivedProp

我也尝试在类中添加一个ShouldSerializeProp方法和一个PropSpecified属性Derived,但是都没有用。我尝试设置断点以查看是否调用了断点,而未调用断点。

事实证明,XmlSerializer正在查看Prop属性在类层次结构中首次出现的原始类,以决定是否序列化属性。为了能够在派生类中控制序列化,首先我必须virtual ShouldSerializePropBase类中添加一个。

public class Base
{
    .....
    public virtual bool ShouldSerializeProp() {return true;}
}
Run Code Online (Sandbox Code Playgroud)

然后,我可以ShouldSerializePropDerived该类中重写并返回false。

public class Derived: Base
{
    .....
    public override bool ShouldSerializeProp() {return false;}
}
Run Code Online (Sandbox Code Playgroud)

这种模式允许不同的派生类从要序列化的父类中选择哪些属性。希望这可以帮助。