Name 成员:
<xs:complexType name="Item" />
在以下 #2 的条件中,有条件地包括了 Name
成员:
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Name" type="xs:string" />
</xs:sequence>
差异是由于XmlSerializer
和xsd.exe
都执行静态类型分析而不是动态代码分析。 两个工具都无法确定情况#2中的Name
属性将始终被跳过,因为两个工具都不尝试反编译ShouldSerializeName()
的源代码以证明它总是返回false
。 因此,Name
将出现在版本#2的模式中,尽管实际上从未出现过。 如果您随后创建Web服务并使用{{link1: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;
public int Id {
get {
return this.idField;
}
set {
this.idField = value;
}
}
[System.Xml.Serialization.XmlIgnoreAttribute()]
public bool IdSpecified {
get {
return this.idFieldSpecified;
}
set {
this.idFieldSpecified = value;
}
}
}
有关绑定到条件序列化值成员的更多详细信息,请参阅XML Schema绑定支持:MinOccurs属性绑定支持和ShouldSerialize*() vs *Specified条件序列化模式。
那么这就是主要区别,但也有次要差异可能会影响你的选择:
[XmlIgnore]
不能在派生类中被覆盖,但是当标记为虚拟的时候ShouldSerializeXXX()
可以被覆盖; 请参见 这里 中的示例。
如果一个成员无法被XmlSerializer
序列化,因为它引用了缺乏无参数构造函数的类型,那么将该成员标记为[XmlIgnore]
将允许包含的类型被序列化——而添加 ShouldSerializeXXX() { return false; }
则不会允许包含类型被序列化,因为如前所述,XmlSerializer
只执行静态类型分析。例如以下内容:
public class RootObject
{
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和protobuf-net。
如在注释中提到的那样,在Visual Studio中重命名有条件地进行序列化的属性不会自动重命名相应的ShouldSerializeXXX()
方法名称,这可能会导致潜在的维护问题。
ShouldSerializeXXX
是一个糟糕的想法。 - scher