ShouldSerialize * () vs * Conditional conditional serialization pattern

I know both the ShouldSerialize * pattern and the given pattern and how they work, but is there any difference between them?

Are there any "gotchas" using one method against another when certain things need to be serialized conditionally?

This question is specific to the use of XmlSerializer , but general information on this topic is also welcome.

There is very little information on this topic, so it may be because they fulfill the same goal, and this is the choice of style. However, it seems strange that .NET developers analyzed the class using reflection and looked for both / both patterns to determine how the generated serializer behaves, since it slows down the generation of the serializer if it is not just an artifact of backward compatibility.

EDIT: For those unfamiliar with the two patterns, if the *Specified property or ShouldSerialize* method returns true, then this 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); } 
+12
c # serialization xml-serialization xmlserializer
Jun 15 '16 at 2:43 on
source share
2 answers

The purpose of the {propertyName}Specified template is described in XML Schema Binding Support: MinOccurs Attribute Binding Support . It has been added to support the XSD schema element, in which:

  • The <element> .
  • minOccurs is zero.
  • The maxOccurs attribute specifies a single instance.
  • The data type is converted to a value type.

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 keeps track of whether the element was encountered in XML , and must be serialized back to XML. If an element occurs, {propertyName}Specified set to true , otherwise false . In this way, the deserialized instance can determine whether the property was canceled or its default value was explicitly set during deserialization.

The reverse is also implemented to generate the circuit. If you define a C # type with a couple of properties matching the pattern above, use xsd.exe to create the corresponding XSD file, the corresponding minOccurrs will be added to the schema. For example, the following type is specified:

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

The following scheme will be created 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 although xsd.exe is only documented to automatically create the {propertyName}Specified for value type properties, XmlSerializer will respect the pattern when used manually for properties of a reference type.

You may ask why xsd.exe not associated with a null value in this case? Perhaps because:

You need to know this template because xsd.exe sometimes generates it for you automatically, however, the interaction between the property and the Specified property is strange and capable of creating errors. You can populate all the properties in your class and then serialize the XML and lose everything, because you also did not set the corresponding Specified properties to true . This "gotcha" appears here from time to time here, see, for example, this question or this one too .

Another “obtained” with this template is that if you need to serialize your type using a serializer that does not support this template, you can > manually disable the output of this property during serialization, and it will probably be necessary to manually set it during deserialization. Since each serializer may have its own mechanism for suppressing properties (or no mechanism at all!), This can become more and more burdensome over time.

(Finally, I'm a little surprised that your MyPropertySpecified runs successfully without a setter. I seem to recall a version of .Net 2.0 in which the missing {propertyName}Specified installer can throw an exception. It no longer plays in later versions, and I don't have 2.0 for testing. So this may be the third question.)

Support for the ShouldSerialize{PropertyName}() method is documented in Properties in Windows Forms Controls: Defining Default Values ​​Using the ShouldSerialize and Reset Methods. As you can see, the documentation is located in the Windows Forms section of MSDN, and not in the XmlSerializer section, so this is essentially a semi-closed functionality. I do not know why support for this method and the Specified property exist in the XmlSerializer . ShouldSerialize was introduced in .NET 1.1 , and I believe that MinOccurs binding support was added in .NET 2.0 , so maybe the earlier functionality didn't quite meet the needs (or taste) of the xsd.exe development xsd.exe ?

Since this is a method, not a property, it lacks the "gotchas" template {propertyName}Specified . It also seems more popular in practice and has been adopted by other serializers, including:

So which template to use?

  • If xsd.exe automatically generates the {propertyName}Specified , or your type should monitor whether or not a particular element has appeared in the XML file, or if you need an XSD auto-generator to indicate that a specific value is optional, use this template and watch for "gotchas".

  • Otherwise, use the template ShouldSerialize{PropertyName}() . It has fewer errors and can be more widely supported.

+15
Jun 15 '16 at 18:20
source share

To add to @dbc’s very detailed answer, I ran into the problem of managing serialization in derived classes. In my situation, I had a base class and a derived class in which the 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 is computed in the derived class, for the Derived class, I wanted to serialize Comp1 and Comp2 , but not Prop . It turns out that setting the XmlIgnore attribute in the Prop property in the Derived class does not work, and Prop serialized.

I also tried adding the ShouldSerializeProp method and the PropSpecified property in the Derived class, but it does not work. I tried to set breakpoints to see if they were called, but they weren’t.

It turns out that the XmlSerializer looking at the source class, where the Prop property first appears in the class hierarchy to decide whether to serialize the property or not. To be able to control serialization in a derived class, I first had to add virtual ShouldSerializeProp to the Base class.

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

Then I could override ShouldSerializeProp in the Derived class and return false.

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

This template allows various derived classes to choose which properties from the parent class they serialize. Hope this helps.

+1
Jan 10 '18 at 6:48
source share



All Articles