您的服务器使用JavaScriptSerializer 格式输出TimeSpan,而您的客户端期望Json.NET 格式的时间跨度。
通过测试可以看到两种格式,如下:
Debug.WriteLine(new JavaScriptSerializer().Serialize(DateTime.Today - DateTime.Today.AddDays(-1)))
哪个打印:
{"Ticks":864000000000,"Days":1,"Hours":0,"Milliseconds":0,"Minutes":0,"Seconds":0,"TotalDays":1,"TotalHours":24,"TotalMilliseconds":86400000,"TotalMinutes":1440,"TotalSeconds":86400}
对比
Debug.WriteLine(JsonConvert.SerializeObject(DateTime.Today - DateTime.Today.AddDays(-1)))
打印更简洁:
"1.00:00:00"
让 Json.NET 以 JavaScriptSerializer 格式解析 TimeSpan 对象非常容易,但您的要求正好相反:以 Json.NET 格式输出 JavaScriptSerializer。这更加困难,因为 Json.NET 将时间跨度表示为一个简单的字符串,但 JavaScriptConverter 只允许将类型映射到自定义 JSON 对象——而不是字符串。
因此,有必要将转换器应用于包含TimeSpan 的所有类型。这是一个通用转换器,它执行默认转换,然后覆盖所有TimeSpan-valued 属性和字段。它对于较小的类(例如您的示例类)将是高性能的,但对于序列化大型根类的性能较差,您可能需要为此编写自定义转换器:
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;
}
}
然后像这样使用它:
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
要在 ASP.NET 服务中配置转换器,请参阅How to: Configure ASP.NET Services in ASP.NET AJAX。