为什么我不能用Json.Net反序列化这个自定义结构? [英] Why can I not deserialize this custom struct using Json.Net?

查看:229
本文介绍了为什么我不能用Json.Net反序列化这个自定义结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个表示的DateTime其中也有区域信息如下一个结构:

 公共结构DateTimeWithZone 
{
私人只读的DateTime _utcDateTime;
私人只读的TimeZoneInfo _timezone;
公共DateTimeWithZone(DateTime的日期时间,的TimeZoneInfo的时区,
DateTimeKind KIND = DateTimeKind.Utc)
{
日期时间= DateTime.SpecifyKind(日期时间,那种);
_utcDateTime = dateTime.Kind!= DateTimeKind.Utc
? TimeZoneInfo.ConvertTimeToUtc(日期时间,的timeZone)
:日期时间;
_timezone =的timeZone;
}
公众的DateTime UniversalTime {{返回_utcDateTime; }}
公众的TimeZoneInfo时区{{返回_timezone; }}
公众的DateTime本地时间
{
得到
{
返回TimeZoneInfo.ConvertTime(_utcDateTime,_timezone);
}
}
}



我可以使用序列化对象

  VAR现在= DateTime.Now; 
变种dateTimeWithZone =新DateTimeWithZone(现在TimeZoneInfo.Local,DateTimeKind.Local);
VAR serializedDateTimeWithZone = JsonConvert.SerializeObject(dateTimeWithZone);



但是,当我反序列化使用下面的,我得到一个无效的日期时间值(DateTime.MinValue)

  VAR deserializedDateTimeWithZone = JsonConvert.DeserializeObject< DateTimeWithZone>(serializedDateTimeWithZone); 



任何帮助深表感谢。


解决方案

您需要编写一个定制 JsonConverter 正确序列化和反序列化这些值。这个类添加到您的项目

 公共类DateTimeWithZoneConverter:JsonConverter 
{
公众覆盖布尔CanConvert(键入的objectType)
{
返回的objectType == typeof运算(DateTimeWithZone)|| ==的objectType typeof运算(DateTimeWithZone?);
}

公共覆盖无效WriteJson(JsonWriter作家,对象的值,JsonSerializer串行)
{
VAR DTWZ =(DateTimeWithZone)值;

writer.WriteStartObject();
writer.WritePropertyName(UniversalTime);
serializer.Serialize(作家,dtwz.UniversalTime);
writer.WritePropertyName(时区);
serializer.Serialize(作家,dtwz.TimeZone.Id);
writer.WriteEndObject();
}

公众覆盖对象ReadJson(JsonReader读者,类型的objectType,对象existingValue,JsonSerializer串行)
{
变种UT =默认值(日期时间);
变种TZ =默认(的TimeZoneInfo);
VAR gotUniversalTime = FALSE;
VAR gotTimeZone = FALSE;
,而(reader.Read())
{
如果(reader.TokenType = JsonToken.PropertyName!)
中断;

VAR propertyName的=(字符串)reader.Value;
如果(reader.Read()!)
继续;

如果(propertyName的==UniversalTime)
{
UT = serializer.Deserialize<&日期时间GT;(读卡器);
gotUniversalTime = TRUE;
}

如果(propertyName的==时区)
{
VAR TZID = serializer.Deserialize<串GT;(读卡器);
TZ = TimeZoneInfo.FindSystemTimeZoneById(TZID);
gotTimeZone = TRUE;
}
}

如果((gotUniversalTime&安培;!&安培; gotTimeZone))
{
抛出新InvalidDataException(一个DateTimeWithZone必须包含UniversalTime和TimeZone的性质)。
}

返回新DateTimeWithZone(UT,TZ);
}
}



然后你使用JSON的设置进行注册。例如,默认设置是可以改变的是这样的:

  JsonConvert.DefaultSettings =()=> 
{
VAR设置=新JsonSerializerSettings();
settings.Converters.Add(新DateTimeWithZoneConverter());
返回设置;
};



然后,它会正常序列化到一个可用的格式。例如:



<预类=郎JSON prettyprint-覆盖> {
UniversalTime:2014-07-13T20:24 :40.4664448Z,
时区:太平洋标准时间
}

和将反序列化正确的为好。



如果您要包括本地时间,您只能将它添加到 WriteJson 方法,但反序列化时,它也许应该被忽略。否则,你就会有真理的两个不同的来源。只能有一个权威的。



此外,您可能会改为尝试野田佳彦时间,其中包括一个 ZonedDateTime 结构这一确切目的。还有的已经是系列化通过的 NodaTime.Serialization.JsonNet 的NuGet包。


I have a struct representing a DateTime which also has zone info as below:

public struct DateTimeWithZone
{
    private readonly DateTime _utcDateTime;
    private readonly TimeZoneInfo _timeZone;    
    public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone, 
                        DateTimeKind kind = DateTimeKind.Utc)
    {
        dateTime = DateTime.SpecifyKind(dateTime, kind);        
        _utcDateTime = dateTime.Kind != DateTimeKind.Utc 
                      ? TimeZoneInfo.ConvertTimeToUtc(dateTime, timeZone) 
                      : dateTime;
        _timeZone = timeZone;
    }    
    public DateTime UniversalTime { get { return _utcDateTime; } }
    public TimeZoneInfo TimeZone { get { return _timeZone; } }
    public DateTime LocalTime 
    { 
        get 
        { 
            return TimeZoneInfo.ConvertTime(_utcDateTime, _timeZone); 
        } 
    }
}

I can serialize the object using:

var now = DateTime.Now;
var dateTimeWithZone = new DateTimeWithZone(now, TimeZoneInfo.Local, DateTimeKind.Local);
var serializedDateTimeWithZone = JsonConvert.SerializeObject(dateTimeWithZone);

But when I deserialize it using the below, I get an invalid DateTime value (DateTime.MinValue)

var deserializedDateTimeWithZone = JsonConvert.DeserializeObject<DateTimeWithZone>(serializedDateTimeWithZone);

Any help is much appreciated.

解决方案

You need to write a custom JsonConverter to properly serialize and deserialize these values. Add this class to your project.

public class DateTimeWithZoneConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof (DateTimeWithZone) || objectType == typeof (DateTimeWithZone?);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var dtwz = (DateTimeWithZone) value;

        writer.WriteStartObject();
        writer.WritePropertyName("UniversalTime");
        serializer.Serialize(writer, dtwz.UniversalTime);
        writer.WritePropertyName("TimeZone");
        serializer.Serialize(writer, dtwz.TimeZone.Id);
        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var ut = default(DateTime);
        var tz = default(TimeZoneInfo);
        var gotUniversalTime = false;
        var gotTimeZone = false;
        while (reader.Read())
        {
            if (reader.TokenType != JsonToken.PropertyName)
                break;

            var propertyName = (string)reader.Value;
            if (!reader.Read())
                continue;

            if (propertyName == "UniversalTime")
            {
                ut = serializer.Deserialize<DateTime>(reader);
                gotUniversalTime = true;
            }

            if (propertyName == "TimeZone")
            {
                var tzid = serializer.Deserialize<string>(reader);
                tz = TimeZoneInfo.FindSystemTimeZoneById(tzid);
                gotTimeZone = true;
            }
        }

        if (!(gotUniversalTime && gotTimeZone))
        {
            throw new InvalidDataException("An DateTimeWithZone must contain UniversalTime and TimeZone properties.");
        }

        return new DateTimeWithZone(ut, tz);
    }
}

Then register it with the json settings you're using. For example, the default settings can be changed like this:

JsonConvert.DefaultSettings = () =>
{
    var settings = new JsonSerializerSettings();
    settings.Converters.Add(new DateTimeWithZoneConverter());
    return settings;
};

Then it will properly serialize to a usable format. Example:

{
  "UniversalTime": "2014-07-13T20:24:40.4664448Z",
  "TimeZone": "Pacific Standard Time"
}

And it will deserialize properly as well.

If you want to include the local time, You would just add that to the WriteJson method, but it should probably be ignored when deserializing. Otherwise you'd have two different sources of truth. Only one can be authoritative.

Also, you might instead try Noda Time, which includes a ZonedDateTime struct for this exact purpose. There's already support for serialization via the NodaTime.Serialization.JsonNet NuGet package.

这篇关于为什么我不能用Json.Net反序列化这个自定义结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆