I am aware of both of the ShouldSerialize* pattern and the *Specified pattern and how they work, but is there any difference between the two?
Are there any "gotchas" using one method vs the other when certain things should be serialized conditionally?
This question is specific to the usage of XmlSerializer
, but general information regarding this topic is welcome as well.
There is very little information on this topic out there, so it may be because they perform the exact same purpose and it's a style choice. However, it seems strange that the .NET implementers would analyze the class via reflection and look for either/both patterns to determine how the generated serializer behaves since it slows down the generation of the serializer unless it's just a backwards compatibility artifact.
EDIT: For those unfamiliar with the two patterns if either the *Specified
property or ShouldSerialize*
method returns true, then that property is serialized.
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);
}
The intent of the {propertyName}Specified
pattern is documented in XML Schema Binding Support: MinOccurs Attribute Binding Support. It was added to support an XSD schema element in which:
<element>
element is involved.In this case, xsd.exe /classes
will automatically generate (or you can manually generate) a property with the same name as the schema element and a {propertyName}Specified
boolean get/set property that tracks whether the element was encountered in the XML and should be serialized back to XML. If the element is encountered, {propertyName}Specified
is set to true
, otherwise false
. Thus the deserialized instance can determine whether the property was unset (rather than explicitly set to its default value) in the original XML.
The inverse is also implemented for schema generation. If you define a C# type with a pair of properties matching the pattern above, then use xsd.exe
to generate a corresponding XSD file, an appropriate minOccurrs
will be added to the schema. For instance, given the following type:
public class ExampleClass
{
[XmlElement]
public decimal Something { get; set; }
[XmlIgnore]
public bool SomethingSpecified { get; set; }
}
The following schema will be generated, and vice versa:
<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>
Note that, while xsd.exe
is documented only to generate automatically a {propertyName}Specified
property for value type properties, XmlSerializer
will respect the pattern when used manually for reference type properties.
You might ask, why does xsd.exe
not bind to a Nullable<T>
in this case? Perhaps because:
xsi:nil="true"
attribute instead. See Xsi:nil Attribute Binding Support.You need to be aware of this pattern because xsd.exe
will sometimes generate it for you automatically, however the interaction between a property and its Specified
property is weird and liable to produce bugs. You can fill up all the properties in your class, then serialize to XML and lose everything because you didn't also set set the corresponding Specified
properties to true
. This "gotcha" comes up here from time to time here, see e.g. this question or this one also.
Another "gotcha" with this pattern is that, if you need to serialize your type with a serializer that does not support this pattern, you may want to manually suppress output of this property during serialization, and probably will need to manually set it during deserialization. Since each serializer may have its own custom mechanism for suppressing properties (or no mechanism at all!), doing this can become more and more burdensome over time.
(Finally, I'm a little surprised that your MyPropertySpecified
works successfully without a setter. I seem to recall a version of .Net 2.0 in which a missing {propertyName}Specified
setter would cause an exception to be thrown. But it's no longer reproducible on later versions, and I don't have 2.0 to test. So that might be a third gotcha.)
Support for the ShouldSerialize{PropertyName}()
method is documented in Properties in Windows Forms Controls: Defining Default Values with the ShouldSerialize and Reset Methods. As you can see the documentation is in the Windows Forms section of MSDN not the XmlSerializer
section, so it is, in fact, semi-hidden functionality. I have no idea why support for this method and the Specified
property both exist in XmlSerializer
. ShouldSerialize
was introduced in .Net 1.1 and I believe that MinOccurs binding support was added in .Net 2.0, so perhaps the earlier functionality didn't quite meet the needs (or taste) of the xsd.exe
development team?
Because it is a method not a property, it lacks the "gotchas" of the {propertyName}Specified
pattern. It also seems to be more popular in practice, and has been adopted by other serializers including:
So, which pattern to use?
If xsd.exe
generates a {propertyName}Specified
property for you automatically, or your type needs to track whether a specific element appeared or not in the XML file, or you need your auto-generated XSD to indicate that a certain value is optional, use this pattern and watch out for the "gotchas".
Otherwise, use the ShouldSerialize{PropertyName}()
pattern. It has fewer gotchas and may be more widely supported.
To add to the very detailed answer by @dbc, I ran into an issue with managing serialization in derived classes. In my situation I had a base class and a derived class where a Prop
property was overridden.
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 {}}
}
Since the Prop
property in the derived class is calculated, for the Derived
class I wanted to serialize Comp1
and Comp2
but not Prop
. It turns out that setting the XmlIgnore
attribute on the Prop
property in the Derived
class does not work and Prop
is serialized anyway.
I also tried to add a ShouldSerializeProp
method and a PropSpecified
property in the Derived
class, but neither work. I tried setting breakpoints to see if they are called and they are not.
It turns out that the XmlSerializer
is looking at the original class where the Prop
property appears for the first time in the class hierarchy to decide whether to serialize a property or not. To be able to control serialization in a derived class, first I had to add a virtual ShouldSerializeProp
in the Base
class.
public class Base
{
.....
public virtual bool ShouldSerializeProp() {return true;}
}
Then I could override the ShouldSerializeProp
in the Derived
class and return false.
public class Derived: Base
{
.....
public override bool ShouldSerializeProp() {return false;}
}
This pattern allows different derived classes to choose what properties from the parent class they serialize. Hope this helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With