I have looked for examples to ignore a property of a class during xml serialization and deserialization. I have found three different methods and can't figure out, when they should be used. My special interest is, which one works with XmlSerializer
better.
XmlIgnore
attribute
public class Item
{
[XmlIgnore]
public string Name { get; set; }
}
Method beginning with ShouldSerialize...
public class Item
{
public string Name { get; set; }
public bool ShouldSerializeName()
{
return false;
}
}
NonSerialized
attribute
public class Item
{
[NonSerialized]
public string Name { get; set; }
}
Where there is some explanation about the difference between XmlIgnoreAttribtue
and NonSerializedAttribute
on stackoverflow and msdn, I was not able to find information about when to use XmlIgnoreAttribtue
and when the ShouldSerializeXXX
pattern. I tried both of them with the XmlSerializer and both of them see work as expected.
The basic difference between #1 and #2 is that they generate different XML Schemas. If you want a member to be excluded from your type's schema, use [XmlIgnore]
. If you want a member to be included conditionally, use ShouldSerializeXXX()
or XXXSpecified
. (Finally, as stated in this answer, [NonSerialized]
in option #3 is ignored by XmlSerializer
.)
To see the difference between #1 and #2, you can use xsd.exe
to generate schemas for your types. The following schema is generated for version #1 and completely omits the Name
member:
<xs:complexType name="Item" />
While the following for #2 conditionally includes the Name
member:
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Name" type="xs:string" />
</xs:sequence>
The difference arises because XmlSerializer
and xsd.exe
both perform static type analysis rather than dynamic code analysis. Neither tool can determine that the Name
property in case #2 will always be skipped, because neither tool attempts to decompile the source code for ShouldSerializeName()
to prove it always returns false
. Thus Name
will appear in the schema for version #2 despite never appearing in practice. If you then create a web service and publish your schema with WSDL (or simply make them available manually), different clients will be generated for these two types -- one without a Name
member, and one with.
An additional complexity can arise when the property in question is of a non-nullable value type. Consider the following three versions of Item
. Firstly, a version with an unconditionally included value property:
public class Item
{
public int Id { get; set; }
}
Generates the following schema with Id
always present:
<xs:complexType name="Item">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="Id" type="xs:int" />
</xs:sequence>
</xs:complexType>
Secondly, a version with an unconditionally excluded value property:
public class Item
{
[XmlIgnore]
public int Id { get; set; }
}
Generates the following schema that completely omits the Id
property:
<xs:complexType name="Item" />
And finally a version with a conditionally excluded value property:
public class Item
{
public int Id { get; set; }
public bool ShouldSerializeId()
{
return false;
}
}
Generates the following schema with Id
only conditionally present:
<xs:complexType name="Item">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Id" type="xs:int" />
</xs:sequence>
</xs:complexType>
Schema #2 is as expected, but notice there is a difference between #1 and #3: the first has minOccurs="1"
while the third has minOccurs="0"
. The difference arises because XmlSerializer
is documented to skip members with null
values by default, but has no similar logic for non-nullable value members. Thus the Id
property in case #1 will always get serialized, and so minOccurs="1"
is indicated in the schema. Only when conditional serialization is enabled will minOccurs="0"
be generated. If the third schema is in turn used for client code generation, an IdSpecified
property will be added to the auto-generated code to track whether the Id
property was actually encountered during deserialization:
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;
}
}
}
For more details on binding to conditionally serialized value members, see XML Schema Binding Support: MinOccurs Attribute Binding Support and ShouldSerialize*() vs *Specified Conditional Serialization Pattern.
So that's the primary difference, but there are secondary differences as well that may influence which you choose:
[XmlIgnore]
cannot be overridden in a derived class, but ShouldSerializeXXX()
can be when marked as virtual; see here for an example.
If a member cannot be serialized by XmlSerializer
because, for instance, it refers to a type that lacks a parameterless constructor, then marking the member with [XmlIgnore]
will allow the containing type to be serialized - while adding a ShouldSerializeXXX() { return false; }
will NOT allow the containing type to be serialized, since as stated previously XmlSerializer
only performs static type analysis. E.g. the following:
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; }
}
cannot be serialized by XmlSerializer
.
[XmlIgnore]
is specific to XmlSerializer
, but ShouldSerializeXXX()
is used by other serializers including Json.NET and protobuf-net.
As mentioned in comments, renaming a conditionally serialized property in Visual Studio does not automatically rename the corresponding ShouldSerializeXXX()
method name, leading to potential maintenance gotchas down the road.