我有一个具有一些时间跨度的对象,我在我的MVC Action中使用.net序列化器。(我的动作返回JsonResult类型) timeSpan Prop反序列化如下:
"EndTime":{"Hours":9,"Minutes":0,"Seconds":0,"Milliseconds":0,"Ticks":324000000000,"Days":0,"TotalDays":0.375,"TotalHours":9,"TotalMilliseconds":32400000,"TotalMinutes":540,"TotalSeconds":32400}
之后我在Windows应用程序中调用此操作,我需要使用newtonesoft反序列化此对象。 但我发现了这个错误:
无法将当前JSON对象(例如{“name”:“value”})反序列化为类型'system.Nullable [System.TimeSpan]',因为该类型需要JSON原语值(例如string,number,boolean,null)要正确反序列化。修复此错误要么将JSON更改为JSON原始值(例如string,number,boolean,null),要么更改反序列化类型以使其成为普通的.NET类型(例如,不是像整数这样的基本类型,而不是类似于数组或List的集合,可以从JSON对象反序列化。 JsonObjectAttribute也可以添加到类型中以强制它从JSON对象反序列化。路径'InTime.Hours'第1行
重要的是要知道,我的服务器项目必须只使用microsoft .net序列化程序,win app必须只使用Newtonesoft解串器!
毕竟我猜,如果是序列化程序,请将名称:值问题解决, 也许像这样的事情
{"EndTime":"090000"}
但我不知道如何强制序列化程序更改TimeSpan格式!
也许像这样的事情???? :-):public class Attendance
{
public int ID { get; set; }
public int PID { get; set; }
[Define Format in serialization here (maybe !)]
public System.TimeSpan time { get; set; }
}
答案 0 :(得分:2)
您的服务器正在使用TimeSpan
格式输出JavaScriptSerializer
,而您的客户希望时间范围为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"
以TimeSpan
格式制作Json.NET解析JavaScriptSerializer
对象会非常容易,但您的请求是相反的:以Json.NET格式输出JavaScriptSerializer
。这更难,因为Json.NET将时间跨度表示为简单字符串,但JavaScriptConverter
仅允许将类型映射到自定义 JSON对象 - 而不是字符串
因此,有必要将转换器应用于包含 a TimeSpan
的所有类型。这是一个通用转换器,它执行默认转换,然后覆盖所有TimeSpan
值的属性和字段。它将适用于较小的类(例如您的示例类),但序列化大型根类的性能较差,您可能需要编写自定义转换器:
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。