在.net 4.5标准序列化程序

时间:2015-06-27 14:15:58

标签: json asp.net-mvc serialization json.net deserialization

我有一个具有一些时间跨度的对象,我在我的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; }

}

1 个答案:

答案 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