다음을 통해 공유


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; } }

}

Comments

  • Anonymous
    June 30, 2010
    In my case I've to expose a web service with parameter of Type Employee for webmethod for e.g. SaveEmployee(Employee objEmployee) Even though I assign the parameters properly I am getting null value for BirthDate inside webmethod but other reference type variables for e.g. say string EmployeeName is assigned correctly... Please advise

  • Anonymous
    July 08, 2010
    Hi Jack, You probably first want to check that BirthDate is being sent at all. Having unassigned parameters in a webmethod is typically an indication of a mismatch between client and service contracts.

  • Anonymous
    October 07, 2010
    Hi Youssefm, this is really really useful. Thanks.

  • Anonymous
    September 13, 2011
    How about string elements which by default is serialized as optional? Can I in any way indicate these to mandatory (minOccurs=1).

  • Anonymous
    April 06, 2015
    Thank you very much! This is very helpful :)

  • Anonymous
    September 01, 2015
    Seems like kind of an odd way to specify the minOccurs attribute, but it works!  Thanks :)