为什么我不能使用Json.Net反序列化这个自定义结构?

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

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); } } } 

我可以使用以下命令序列化对象:

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

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

 var deserializedDateTimeWithZone = JsonConvert.DeserializeObject(serializedDateTimeWithZone); 

任何帮助深表感谢。

您需要编写自定义JsonConverter以正确序列化和反序列化这些值。 将此类添加到项目中。

 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(reader); gotUniversalTime = true; } if (propertyName == "TimeZone") { var tzid = serializer.Deserialize(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); } } 

然后使用您正在使用的json设置注册它。 例如,默认设置可以像这样更改:

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

然后它将正确序列化为可用的格式。 例:

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

并且它也将正确地反序列化。

如果要包含本地时间,只需将其添加到WriteJson方法中,但在反序列化时可能会忽略它。 否则你会有两种不同的真相来源。 只有一个可以是权威的。

此外,您可以尝试使用Noda Time ,其中包含用于此目的的ZonedDateTime结构。 已经通过NodaTime.Serialization.JsonNet NuGet包支持序列化 。

只需声明构造函数如下,这就是全部

 [JsonConstructor] public DateTimeWithZone(DateTime universalTime, TimeZoneInfo timeZone, DateTimeKind kind = DateTimeKind.Utc) { universalTime = DateTime.SpecifyKind(universalTime, kind); _utcDateTime = universalTime.Kind != DateTimeKind.Utc ? TimeZoneInfo.ConvertTimeToUtc(universalTime, timeZone) : universalTime; _timeZone = timeZone; } 

注意:我只添加了JsonConstructor属性,并将参数名称更改为universalTime