我有一个表示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<DateTimeWithZone>(serializedDateTimeWithZone);
非常感谢任何帮助。
答案 0 :(得分:11)
您需要编写custom 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<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);
}
}
然后使用您正在使用的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
结构,用于此目的。已经通过serialization NuGet包支持NodaTime.Serialization.JsonNet。
答案 1 :(得分:11)
只需将构造函数声明如下,即全部
[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