Your server is outputting the TimeSpan
using JavaScriptSerializer
format whereas your client is expecting the time span in Json.NET format.
You can see the two formats by testing, as follows:
Debug.WriteLine(new JavaScriptSerializer().Serialize(DateTime.Today - DateTime.Today.AddDays(-1)))
which prints:
{"Ticks":864000000000,"Days":1,"Hours":0,"Milliseconds":0,"Minutes":0,"Seconds":0,"TotalDays":1,"TotalHours":24,"TotalMilliseconds":86400000,"TotalMinutes":1440,"TotalSeconds":86400}
Versus
Debug.WriteLine(JsonConvert.SerializeObject(DateTime.Today - DateTime.Today.AddDays(-1)))
which prints a much more concise:
"1.00:00:00"
It would be quite easy to make Json.NET parse TimeSpan
objects in JavaScriptSerializer
format, but your request is the reverse: make JavaScriptSerializer
output in Json.NET format. That's more difficult because Json.NET represents a time span as a simple string, but a JavaScriptConverter
only allows for mapping a type to a custom JSON object -- not a string.
Thus it's necessary to apply a converter to all types containing a TimeSpan
. Here's a generic converter that does a default conversion then overrides all TimeSpan
-valued properties and fields. It will be performant for smaller classes (such as your example class) but will have poor performance for serializing large root classes, for which you might need to write a custom converter:
public class TypeWithTimespanConverter<T> : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get
{
return new[] { typeof(T) };
}
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
// Use a "fresh" JavaScriptSerializer here to avoid infinite recursion.
var hasTimeSpan = (T)obj;
// Generate a default serialization. Is there an easier way to do this?
var defaultSerializer = new JavaScriptSerializer();
var dict = defaultSerializer.Deserialize<Dictionary<string, object>>(defaultSerializer.Serialize(obj));
foreach (var pair in dict.ToList())
{
if (!(pair.Value is IDictionary))
continue;
TimeSpan span;
if (obj.TryGetPropertyValueOfType<TimeSpan>(pair.Key, out span))
{
dict[pair.Key] = span.ToString();
continue;
}
TimeSpan? spanPtr;
if (obj.TryGetPropertyValueOfType<TimeSpan?>(pair.Key, out spanPtr))
{
if (spanPtr == null)
dict[pair.Key] = null;
else
dict[pair.Key] = spanPtr.Value.ToString();
continue;
}
}
return dict;
}
}
public static class ObjectExtensions
{
public static bool TryGetPropertyValueOfType<T>(this object obj, string name, out T value)
{
if (obj == null)
throw new NullReferenceException();
var type = obj.GetType();
var property = type.GetProperty(name, BindingFlags.Public | BindingFlags.Instance);
if (property != null
&& property.GetIndexParameters().Length == 0
&& typeof(T).IsAssignableFrom(property.PropertyType)
&& property.GetGetMethod(false) != null)
{
value = (T)property.GetGetMethod(false).Invoke(obj, new object[0]);
return true;
}
var field = type.GetField(name, BindingFlags.Public | BindingFlags.Instance);
if (field != null
&& typeof(T).IsAssignableFrom(field.FieldType))
{
value = (T)field.GetValue(obj);
return true;
}
value = default(T);
return false;
}
}
And then use it like:
var attendance = new Attendance { ID = 101, PID = -101, time = DateTime.Today - DateTime.Today.AddDays(-1).AddHours(-1) };
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new TypeWithTimespanConverter<Attendance>() });
var json = serializer.Serialize(attendance); // Serialize with JavaScriptSerializer and TypeWithTimespanConverter
Debug.WriteLine(json); // Prints {"ID":101,"PID":-101,"time":"1.01:00:00"}
var attendance2 = JsonConvert.DeserializeObject<Attendance>(json); // Deserialize with Json.NET
Debug.Assert(attendance2.time == attendance.time); // Assert that the timespan was converted
To configure the converter in ASP.NET services, see How to: Configure ASP.NET Services in ASP.NET AJAX.