When trying to serialise your model into Json you would notice the ugly way that the Microsoft JavaScriptserializer outputs dates. ie. {“d”:”\/Date(1240718400000)\/”}
This might not be a problem when you are using JQuery or Javascript, but what if your consumer is, say, an IPhone or another device? Wouldn’t you rather stick to a platform independent and recognised format?
The steps below might be a bit too drastic for your implementations, but follow them and you might also learn how to create your own ValueType and CustomJsonSerialiser all in one go!
This is what we want to accomplish. We only needed to give one converter to our CustomConverterJsonResult, but you could make this a list or even a configuration/ioc injection.
public ActionResult Index(int? id)
{
return new CustomConverterJsonResult (new UnixDateTimeConverter(), _repository.GetPerson(id));
}
Below is our custom ValueType. This allows you to do the following:
UnixDateTime uDateTime = DateTime.Now; //OR DateTime dateTime = uDateTime; //OR > < => and even casting between them
public struct UnixDateTime : IComparable
{
private static DateTime _baseDateTime = new DateTime(1970, 1, 1, 0, 0, 0);
private readonly long _epochSeconds;
public UnixDateTime(DateTime dateTime)
{
_epochSeconds = ConvertToUnixEpochSeconds(dateTime) ?? 0;
}
public UnixDateTime(long epochSeconds)
{
_epochSeconds = epochSeconds;
}
public override bool Equals(object obj)
{
if (!obj.GetType().IsAssignableFrom(typeof(UnixDateTime)))
return false;
return ((UnixDateTime)obj)._epochSeconds.Equals(_epochSeconds);
}
public bool Equals(UnixDateTime other)
{
return other._epochSeconds.Equals(_epochSeconds);
}
public override int GetHashCode()
{
return _epochSeconds.GetHashCode();
}
public int CompareTo(object obj)
{
return _epochSeconds.CompareTo(((UnixDateTime) obj)._epochSeconds);
}
public override string ToString()
{
return _epochSeconds.ToString();
}
public static bool operator >(UnixDateTime source, UnixDateTime target)
{
return source._epochSeconds > target._epochSeconds;
}
public static bool operator <(UnixDateTime source, UnixDateTime target)
{
return source._epochSeconds < target._epochSeconds;
}
public static bool operator ==(UnixDateTime source, UnixDateTime target)
{
return source._epochSeconds == target._epochSeconds;
}
public static bool operator !=(UnixDateTime source, UnixDateTime target)
{
return source._epochSeconds != target._epochSeconds;
}
public static implicit operator UnixDateTime(DateTime dateTime)
{
return new UnixDateTime(dateTime);
}
public static implicit operator UnixDateTime(long value)
{
return new UnixDateTime(value);
}
public DateTime? ToDateTime()
{
return ConvertFromUnixEpochSeconds(_epochSeconds);
}
private static long? ConvertToUnixEpochSeconds(DateTime? date)
{
if (!date.HasValue)
return null;
return (long)((DateTime)date - _baseDateTime).TotalSeconds;
}
private static DateTime? ConvertFromUnixEpochSeconds(long? seconds)
{
if (!seconds.HasValue)
return null;
return _baseDateTime.AddSeconds(seconds.Value);
}
}
We then need a CustomConverterJsonResult. The reason for this is to inject the converters needed (no support for this in the normal JsonResult).
public class CustomConverterJsonResult : JsonResult
{
private readonly JavaScriptConverter _customConverter;
public CustomConverterJsonResult(JavaScriptConverter customConverter, object data)
{
_customConverter = customConverter;
Data = data;
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = !string.IsNullOrEmpty(ContentType) ? ContentType : "application/json";
if (ContentEncoding != null)
response.ContentEncoding = ContentEncoding;
if (Data != null)
{
JavaScriptSerializer serializer = CreateJsonSerializer();
response.Write(serializer.Serialize(Data));
}
}
private JavaScriptSerializer CreateJsonSerializer()
{
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new []{_customConverter});
return serializer;
}
}
And finally… the JavascriptConverter.
public class UnixDateTimeConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if(dictionary != null)
{
var stringValue = string.Empty + dictionary["UnixEpochSeconds"];
if(string.IsNullOrEmpty(stringValue))
return null;
return new UnixDateTime(long.Parse(stringValue));
}
return null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
var result = new Dictionary<string, object> { { "UnixEpochSeconds", obj.ToString() } };
return result;
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(UnixDateTime), typeof(UnixDateTime?) })); }
}
}
This creates the following output:
{"Person":[{"Name":"John Doe","LastUpdated":[{"UnixEpochSeconds":"187653000"}]]}