Serialization of DateTime properties on DateTime default value

Jun 19, 2012 at 12:18 PM

As you know the DateTime type in .NET framework has a default value equals to DateTime.MinValue. Assuming that I have a class with a DateTime property ([YAXFormat("yyyy-MM-dd")), when I serialize an object instance of this class the library exports '0001-01-01'  as its value. I would need the library to treat these kind of value as null and therefore omit the value in the output.

I know I can work it out something by playing with string-typed public properties instead of working directly with DateTime properties but I'm trying to avoid doing it.

I'm wondering if there is any field-attribute of configuration property that I could use so that the serializer does not export DateTime properties when that property has its default value.

Luis Quijada

P.D.: Congratulations for your library.

Coordinator
Jun 19, 2012 at 4:14 PM

Unfortunately YAXLib does not support conditional field/property ignorance. It cannot also treat default values for value-types as null, because they are valid values; for the same reason that zero is a valid integer. One workaround could be using custom-serializers. I created a general purpose custom serializer for DateTime values in the following code snippet, that does not serialize default values for DateTimes, whether they are going to be serialized as either an attribute, element, or value for another element. I hope it helps:

 

public class DateTimeDefaultSample
{
    [YAXCustomSerializer(typeof(DateCustomSerializer))]
    public DateTime DateAsElem1 { get; set; }

    [YAXCustomSerializer(typeof(DateCustomSerializer))]
    [YAXAttributeForClass]
    public DateTime DateAsAttr1 { get; set; }

    [YAXCustomSerializer(typeof(DateCustomSerializer))]
    [YAXValueFor("Foo/Bar1")]
    public DateTime DateAsValue1 { get; set; }


    [YAXCustomSerializer(typeof(DateCustomSerializer))]
    public DateTime DateAsElem2 { get; set; }

    [YAXCustomSerializer(typeof(DateCustomSerializer))]
    [YAXAttributeForClass]
    public DateTime DateAsAttr2 { get; set; }

    [YAXCustomSerializer(typeof(DateCustomSerializer))]
    [YAXValueFor("Foo/Bar2")]
    public DateTime DateAsValue2 { get; set; }


    public static DateTimeDefaultSample GetSampleInstance()
    {
        return new DateTimeDefaultSample
                    {
                        DateAsElem1 = new DateTime(), 
                        DateAsAttr1 = new DateTime(),
                        DateAsValue1 = new DateTime(),
                        DateAsElem2 = DateTime.Now,
                        DateAsAttr2 = DateTime.Now,
                        DateAsValue2 = DateTime.Now
                    };
    }
}

public class DateCustomSerializer : ICustomSerializer<DateTime>
{
    private readonly DateTime _defaultDateTime = new DateTime();
    private const string Format = "yyyy-MM-dd";

    private static DateTime ConvertFromString(string str)
    {
        DateTime result;
        if (!DateTime.TryParseExact(str, Format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out result))
        {
            // replace it with your desired scenario
            result = new DateTime();
        }

        return result;
    }

    public void SerializeToAttribute(DateTime objectToSerialize, XAttribute attrToFill)
    {
        if(objectToSerialize == _defaultDateTime)
        {
            attrToFill.Remove();
        }

        attrToFill.Value = objectToSerialize.ToString(Format);
    }

    public void SerializeToElement(DateTime objectToSerialize, XElement elemToFill)
    {
        if (objectToSerialize == _defaultDateTime)
        {
            elemToFill.Remove();
        }

        elemToFill.Value = objectToSerialize.ToString(Format);
    }

    public string SerializeToValue(DateTime objectToSerialize)
    {
        if (objectToSerialize == _defaultDateTime)
        {
            // do nothing
            return String.Empty;
        }

        return objectToSerialize.ToString(Format);
    }

    public DateTime DeserializeFromAttribute(XAttribute attrib)
    {
        return ConvertFromString(attrib.Value);
    }

    public DateTime DeserializeFromElement(XElement element)
    {
        return ConvertFromString(element.Value);
    }

    public DateTime DeserializeFromValue(string value)
    {
        return ConvertFromString(value);
    }
}

The DateTimeDefaultSample is the sample class for demonstrating how this works, and the DateCustomSerializer class is the custom serializer itself doing the job.

 

Jun 20, 2012 at 9:30 AM

Of course Sina I see your point, but, let's consider that you work with DBs in .NET projects, typically you don't want your integer fields (nulllable) of a table to be filled with the value of Int32.MinValue if I just want to have a "real" NULL value. If your application need to work with that "default" values then you should take them into account as a "valid" values, in our case we (and sure it should be a common thing in .NET projects) just consider them a null. In addition I don't feel comfortable working with .NET nullable types (Nullable<SomeType>).

The sample provided above help me a lot. Many thanks for your time.

Coordinator
Jun 21, 2012 at 11:53 AM
Edited Jun 21, 2012 at 11:54 AM

I understand. Maybe devising a conditional YAXDontSerialize attribute leads to a better solution. I would check the feasibility of such an approach and will include the result in later releases if it comes out OK.

Jun 22, 2012 at 11:09 AM

Thanks for your time again and for considering this. If I have time to try adapting the current source to cover this need, I'll do it and send to you the modifications done.