【问题标题】:ShouldSerialize*() vs *Specified Conditional Serialization PatternShouldSerialize*() vs *Specified Conditional Serialization Pattern
【发布时间】:2016-10-16 18:11:12
【问题描述】:

我知道 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);
}

【问题讨论】:

  • 什么是“*指定模式”?你的意思是[XmlIgnore] 属性吗?
  • @Sinatr 否。如果您创建一个名为 Specified 的可获取布尔属性,如果它应该被序列化,则返回 true,它似乎与 ShouldSerialize* 模式做同样的事情(显然你放了一个 @此属性的 987654327@ 属性。)
  • 这是故意未记录的,他们希望您改用属性。由于 Winforms 设计器的早期版本或迁移到与 XAML 等效的东西的未完成计划,此“功能”可能会潜入 XmlSerializer。这是一个猜测。
  • @HansPassant 很有趣。 *Specified 模式在XmlSerializer 类文档的备注的控制生成的 XML 部分中简要提到了here,但在 ShouldSerialize*() 模式中没有任何内容。不幸的是,当序列化是有条件的时,属性不起作用。

标签: c# .net serialization xml-serialization xmlserializer


【解决方案1】:

{propertyName}Specified 模式的意图记录在XML Schema Binding Support: MinOccurs Attribute Binding Support 中。添加它是为了支持 XSD 架构元素,其中:

  • 涉及到 <element> 元素。
  • minOccurs 为零。
  • ma​​xOccurs 属性指示单个实例。
  • 数据类型转换为值类型。

在这种情况下,xsd.exe /classes 将自动生成(或者您可以手动生成)一个与架构元素同名的属性和一个 {propertyName}Specified 布尔 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; }
}

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

<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&lt;T&gt;?也许是因为:

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

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

(最后,我有点惊讶你的MyPropertySpecified 在没有设置器的情况下也能成功运行。我似乎记得.Net 2.0 的一个版本,其中缺少{propertyName}Specified 设置器会导致抛出异常。但是它在以后的版本中不再可重现,而且我没有要测试的 2.0。所以这可能是第三个问题。)

ShouldSerialize{PropertyName}() 方法的支持记录在 Properties in Windows Forms Controls: Defining Default Values with the ShouldSerialize and Reset Methods 中。如您所见,文档位于 MSDN 的 Windows 窗体部分而不是 XmlSerializer 部分,因此它实际上是半隐藏功能。我不知道为什么支持这种方法和Specified 属性都存在于XmlSerializer 中。 ShouldSerialize 是在 .Net 1.1 中引入的,我相信 MinOccurs 绑定支持是在 .Net 2.0 中添加的,所以可能早期的功能不能完全满足 @987654361 的需求(或品味) @开发团队?

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

那么,使用哪种模式?

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

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

【讨论】:

  • 感谢您的详细解答!
【解决方案2】:

为了补充@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 {}}
}

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

我还尝试在Derived 类中添加ShouldSerializeProp 方法和PropSpecified 属性,但都不起作用。我尝试设置断点以查看它们是否被调用,而它们不是。

原来XmlSerializer 正在查看Prop 属性第一次出现在类层次结构中的原始类,以决定是否对属性进行序列化。为了能够控制派生类中的序列化,首先我必须在Base 类中添加一个virtual ShouldSerializeProp

public class Base
{
    .....
    public virtual bool ShouldSerializeProp() {return true;}
}

然后我可以覆盖 Derived 类中的 ShouldSerializeProp 并返回 false。

public class Derived: Base
{
    .....
    public override bool ShouldSerializeProp() {return false;}
}

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

【讨论】:

  • 好点@Tibi。几年前我问过这个问题,但从那时起我学到的是总是让像这样的序列化类是密封的,除非你事先知道由于需要虚拟方法而会有派生类。
猜你喜欢
  • 2011-04-16
  • 1970-01-01
  • 1970-01-01
  • 2017-11-26
  • 2021-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多