Jesus and serializers

 Published On

A colleague of mine shared with me an interesting case on DataContractJsonSerializer and DataContractSerializer. The difference between these two classes can be resumed roughly as “one works with JSON and the other one with XML”.

Brief description from MSDN :

Class name Description
DataContractJsonSerializer Serializes objects to the JavaScript Object Notation (JSON) and deserializes JSON data to objects. This class cannot be inherited.
DataContractSerializer Serializes and deserializes an instance of a type into an XML stream or document using a supplied data contract. This class cannot be inherited.

The case consists of serializing and deserializing a model -which has a DateTime field- using these two serializers and a memorystream. First let’s create a very simple model class:

Model class


public class Person
{
    public string Name { get; set; }
    public DateTime Birthday { get; set; }

    public override string ToString()
    {
        return string.Format("{0} was born on {1}", Name, Birthday.ToString("yyyy-MM-dd hh:mm:ss"));
    }
}

We’ll also need Serialize and Deserialize methods that are able to handle the model I defined above:

Serialize & Deserialize methods

static void Serialize(XmlObjectSerializer serializer, MemoryStream memoryStream, Person personToSerialize)
{
    serializer.WriteObject(memoryStream, personToSerialize);
    memoryStream.Position = 0;
}

static Person Deserialize(XmlObjectSerializer serializer, MemoryStream memoryStream)
{
    var deserializedPerson = serializer.ReadObject(memoryStream) as Person;
    return deserializedPerson;
}

Please note the type of serializer parameter in both of the methods. XmlObjectSerializer is the base class for both DataContractJsonSerializer and DataContractSerializer and they are defined like the following :

public sealed class DataContractJsonSerializer : XmlObjectSerializer {}
public sealed class DataContractSerializer : XmlObjectSerializer {}
public abstract class XmlObjectSerializer
{
    public abstract void WriteStartObject(XmlDictionaryWriter writer, object graph);
    public abstract void WriteObjectContent(XmlDictionaryWriter writer, object graph);
    public abstract void WriteEndObject(XmlDictionaryWriter writer);
    ......
}

Whole picture

When we put it all together

static void Main(string[] args)
{
    var serializer = new DataContractSerializer(typeof(Person));
    var jesus = new Person { Name = "Jesus", Birthday = new DateTime(1, 1, 1, 0, 0, 0) };

    Person deserializedJesus = null;
    using (var memoryStream = new MemoryStream())
    {
        Serialize(serializer, memoryStream, jesus);
        deserializedJesus = Deserialize(serializer, memoryStream);
    }
    Console.WriteLine(deserializedJesus);
}

and run the program. We get the following output :

Jesus was born on 0001-01-01 12:00:00
Press any key to continue . . .

That is fine, let’s change now the serializer from DataContractSerializer to DataContractJsonSerializer by replacing the following line

var serializer = new DataContractSerializer(typeof(Person));

by

var serializer = new DataContractJsonSerializer(typeof(Person));

We re-run the program and surprise!

Unhandled Exception: System.Runtime.Serialization.SerializationException: DateTime values that are greater than DateTime.MaxValue or smaller than DateTime.MinValue when converted to UTC cannot be serialized to JSON. ---> System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: value
   --- End of inner exception stack trace ---
   at System.Runtime.Serialization.Json.JsonWriterDelegator.WriteDateTimeInDefaultFormat(DateTime value)
   at System.Runtime.Serialization.Json.JsonWriterDelegator.WriteDateTime(DateTime value)
   at WritePersonToJson(XmlWriterDelegator , Object , XmlObjectSerializerWriteContextComplexJson , ClassDataContract , XmlDictionaryString[] )
   at System.Runtime.Serialization.Json.JsonClassDataContract.WriteJsonValueCore(XmlWriterDelegator jsonWriter, Object obj, XmlObjectSerializerWriteContextComplexJson context, RuntimeTypeHandle declaredTypeHandle)

Visibly DataContractJsonSerializer doesn’t like Jesus :) .

What happened here?

The error message is quite clear despite the fact that it is strangely formulated:

DateTime values that are greater than DateTime.MaxValue or smaller than DateTime.MinValue 
when converted to UTC cannot be serialized to JSON.

At one point, DataContractJsonSerializer takes the DateTime value -Jesus’ birthday- we affected and it tries to convert this one to UTC and the result is not within [DateTime.Min,DateTime.Max]. Let’s gather all the information we have :

  • The time zone I am in is UTC + 1.

Local time to utc

So the code does the equivalent of new DateTime(1, 1, 1, 0, 0, 0).Add(-1) which is lower than DateTime.Min constant value hence the reason of the exception we got.

How to fix it ?

The fix would be to directly assign UTC value to Jesus’ birthday by replacing the following line

Birthday = new DateTime(1, 1, 1, 0, 0, 0)

with

Birthday = DateTime.SpecifyKind(new DateTime(1, 1, 1, 0, 0, 0), DateTimeKind.Utc)

And when we run the program, we get :

Jesus was born on 0001-01-01 12:00:00
Press any key to continue . . .

Why this bug is a sneaky ?

Because depending on the time zone you are in, you may or not encounter the bug. If you are in UTC+0, the program will work just fine for you with both of the serializers.

World time zone map

Important notes

  1. The source code for this article is available on GitHub
  2. Do not use the code snippets given in this article in production code. You might want to add guard clauses against null or unwanted values and add unit tests with edge cases - we worked on lower bound (DateTime.Min), what happens on upper bound (DateTime.Max)? - .

  3. All credits for the time zone map belongs to wikipedia user TimeZonesBoy. The map itself is borrowed from wikipedia

Tags:   .net   c#   exception   json   programming   serializer   xml

Comments:

comments powered by Disqus

© 2017 - Mechanical Object. All rights reserved
Built using Jekyll