How to get minOccurs = 0 in the schema for XmlSerializer struct members

If you try exporting the following type into an XML schema:

 

public class Employee

{

    public DateTime BirthDate { get; set; }

}

 

And you happen to be using XmlSerializer, you may be surprised to find that the schema gets exposed like this:

 

<xs:schema elementFormDefault="qualified" xmlns:xs="https://www.w3.org/2001/XMLSchema">

  <xs:element name="Employee" nillable="true" type="Employee" />

  <xs:complexType name="Employee">

    <xs:sequence>

      <xs:element minOccurs="1" maxOccurs="1" name="BirthDate" type="xs:dateTime" />

    </xs:sequence>

  </xs:complexType>

</xs:schema>

 

Notice that minOccurs gets set to 1. This means that every Employee should have a BirthDate. The reason XmlSerializer does this is because structs cannot be set to null. They must have a value. So XmlSerializer tries to represent this by always setting minOccurs to 1 by default. This prevents miscommunication if the serializer and deserializer have different defaults for a certain type. For example, DateTime’s default constructor set its value to January 1st, Year 1. If a different stack used the Year 2000 as the default value for DateTimes, how would XmlSerializer know how to interpret the value of an Employee that doesn’t have a BirthDate? The serializer would think it was sending a DateTime for Year 2000, and the deserializer would think it was reading a DateTime for Year 1. And XmlSerializer can’t set DateTime to null to indicate that it wasn’t specified. With that said, you may want to force XmlSerializer to emit minOccurs = 0 if you know for a fact that the serializer and deserializer have the same defaults for that type.

 

Here’s how to do it:

 

public class Employee

{

    public DateTime BirthDate { get; set; }

    [XmlIgnore]

    public bool BirthDateSpecified { get; set; }

}

 

The BirthDateSpecified property tells XmlSerializer whether the BirthDate is specified or not. If you exported the schema for this type, you’d get:

 

<xs:schema elementFormDefault="qualified" xmlns:xs="https://www.w3.org/2001/XMLSchema">

  <xs:element name="Employee" nillable="true" type="Employee" />

  <xs:complexType name="Employee">

    <xs:sequence>

      <xs:element minOccurs="0" maxOccurs="1" name="BirthDate" type="xs:dateTime" />

    </xs:sequence>

  </xs:complexType>

</xs:schema>

 

So you essentially create a public field or property with a Boolean type and the name of the struct member plus the string “Specified”. You should also mark it as [XmlIgnore] to make sure that this new member doesn’t get serialized out. The Boolean member then tells the serializer whether the struct member is specified or not. If the Boolean member is false, then XmlSerializer will not serialize out the struct member. So, for example, if you tried serializing this object:

o = new Employee() { BirthDate = new DateTime(1986, 11, 1), BirthDateSpecified = false };

 

BirthDate would not get serialized out, even though you’ve assigned it a value. A helpful tip in this kind of situation is to have BirthDateSpecified’s getter check BirthDate’s value. This way you can avoid serializing out BirthDate only if it corresponds to a certain default value. Or use Nullable<T> in this kind of pattern:

 

public class Employee

{

    public DateTime? BirthDate { get; set; }

    [XmlIgnore]

    public bool BirthDateSpecified { get { return BirthDate != null; } }

}