Можете ли вы рассказать JSON.Net о сериализации DateTime как Utc, даже если это не указано?
Даты в моей базе данных хранятся как Utc. Но когда я возвращаю их с фреймворком сущности, они выходят как неопределенный тип.
Когда JSON.Net сериализует их, они не находятся в формате Utc. Есть ли способ рассказать JSON.Net о сериализации DateTimes как Utc, даже если их тип не указан как Utc?
Ответы
Ответ 1
Установите DateTimeZoneHandling
на JsonSerializerSettings
на Utc
. Это преобразует все даты в UTC перед их сериализацией.
public void SerializeObjectDateTimeZoneHandling()
{
string json = JsonConvert.SerializeObject(
new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Unspecified),
new JsonSerializerSettings
{
DateTimeZoneHandling = DateTimeZoneHandling.Utc
});
Assert.AreEqual(@"""2000-01-01T01:01:01Z""", json);
}
Документация: Настройка DateTimeZoneHandling
Ответ 2
Ответ выше полностью работает, поэтому я использовал его для создания атрибута для преобразования ответа API из PST в UTC.
Сначала мне нужно было создать JsonConverter
public class UTCDateTimeConverter : Newtonsoft.Json.JsonConverter {
private TimeZoneInfo pacificZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
public override bool CanConvert(Type objectType) {
return objectType == typeof(DateTime);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
if (reader.Value == null) return null;
var pacificTime = DateTime.Parse(reader.Value.ToString());
return TimeZoneInfo.ConvertTimeToUtc(pacificTime, pacificZone);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
writer.WriteValue(TimeZoneInfo.ConvertTimeFromUtc((DateTime) value, pacificZone));
}
}
Затем мне пришлось применить это к свойствам, которые необходимо было преобразовать
public class Order{
[JsonConverter(typeof(UTCDateTimeConverter))]
public DateTime OrderDate {get;set;}
}
Ответ 3
Как упомянуто в комментарии @dez, вы можете "пометить" объекты DateTime как UTC непосредственно в коде .net сразу после ЗАГРУЗКИ их из БД и ПЕРЕД сериализацией:
var item = GetItemFromDb(...);
// mark appropriate DateTime fields manually as needed
item.OrderDate = DateTime.SpecifyKind(item.OrderDate, DateTimeKind.Utc);
// now it will be serialized to "2018-10-17T16:21:23.507Z" with the Z at the end
// and javascript will parse it properly and convert to local timezone as needed
Ответ 4
Для меня было проще создать конвертер UTC для свойств DateTime (на основе реализации Newtonsoft.Json.Converters.IsoDateTimeConverter).
public class UtcJsonDateTimeConverter : DateTimeConverterBase
{
private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFZ";
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
string text;
if (value is DateTime dateTime)
{
text = dateTime.ToString(DefaultDateTimeFormat, CultureInfo.InvariantCulture);
}
else
{
throw new JsonSerializationException(
$"Unexpected value when converting date. Expected DateTime or DateTimeOffset, got {value.GetType()}.");
}
writer.WriteValue(text);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
bool nullable = objectType == typeof(DateTime?);
if (reader.TokenType == JsonToken.Null)
{
if (!nullable)
{
throw new JsonSerializationException($"Cannot convert null value to {objectType}.");
}
return null;
}
if (reader.TokenType == JsonToken.Date)
{
return reader.Value;
}
else if (reader.TokenType != JsonToken.String)
{
throw new JsonSerializationException($"Unexpected token parsing date. Expected String, got {reader.TokenType}.");
}
string date_text = reader.Value.ToString();
if (string.IsNullOrEmpty(date_text) && nullable)
{
return null;
}
return DateTime.Parse(date_text, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
}
}
public class SomeEntity
{
[JsonProperty(PropertyName = "id", Order = 1)]
public int ID { get; set; }
[JsonProperty(PropertyName = "created", Order = 2)]
[JsonConverter(typeof(UtcJsonDateTimeConverter))]
public DateTime Created { get; set; }
}