JSON, сгенерированный DataContractJsonSerializer
для DateTimeOffset
и DateTime
, соответствует документации. Из Даты/время и JSON:
DateTimeOffset представлен в JSON как сложный тип: {"DateTime":dateTime,"OffsetMinutes":offsetMinutes}
. Член offsetMinutes
представляет собой смещение местного времени от среднего времени по Гринвичу (GMT), также называемого всемирным скоординированным временем (UTC), связанного с местом интересующего события. Член dateTime
представляет экземпляр во времени, когда произошло интересующее событие (опять же, он становится DateTime в JavaScript, когда используется ASP.NET AJAX, и строкой, когда он не используется). При сериализации элемент dateTime всегда сериализуется по Гринвичу. Таким образом, при описании 3:00 по нью-йоркскому времени dateTime имеет временной компонент 8:00, а offsetMinutes равен 300 (минус 300 минут или 5 часов по Гринвичу).
Примечание
Объекты DateTime и DateTimeOffset при сериализации в JSON сохраняют информацию только с точностью до миллисекунды. Значения доли миллисекунды (микро/наносекунды) теряются во время сериализации.
И из Формат передачи даты и времени:
Значения DateTime отображаются в виде строк JSON в форме "/Date(700000+0500)/"
, где первое число (700000 в приведенном примере) — это количество миллисекунд в часовом поясе по Гринвичу, обычное (не летнее) время с полуночи 1 января 1970 года. Число может быть отрицательным, чтобы представить более ранние времена. Часть, состоящая из +0500 в примере, является необязательной и указывает, что время имеет тип Local, то есть должно быть преобразовано в местный часовой пояс при десериализации. Если он отсутствует, время десериализуется как Utc. Фактическое число (0500 в этом примере) и его знак (+ или -) игнорируются.
Для Newtonsoft см. страницу документации Сериализация дат в JSON для обсуждения как он сериализует даты и время. По умолчанию используются строки формата ISO 8601, но поддерживаются несколько форматов.
Теперь можно настроить формат контракта данных DateTime
, установив DataContractJsonSerializerSettings.DateTimeFormat
:
var settings = new DataContractJsonSerializerSettings
{
DateTimeFormat = new DateTimeFormat("yyyy-MM-ddTHH\\:mm\\:ss.ffFFFFFzzz", CultureInfo.InvariantCulture)
{
},
};
DataContractJsonSerializer serializer = new DataContractJsonSerializer(item.GetType(), settings);
// Remainder as in your question.
Однако результат для DateTimeOffset
выглядит следующим образом:
{"SaveDate":{"DateTime":"2020-06-04T22:00:00.00+00:00","OffsetMinutes":300}}
Это не простая строка, которую вы ищете. Кажется, не существует какого-либо задокументированного способа переопределить формат сериализации для DateTimeOffset
. Демонстрационная скрипта №1 здесь.
Поскольку вы написали: Настоящая проблема, которую я пытаюсь решить, заключается в том, чтобы сериализовать данные, сериализованные DataContractJsonSerializer, для десериализации с помощью метода JsonConvert DeserialzeObject, будет намного проще настроить Json.NET для десериализации DataContractJsonSerializer
формат. Сначала определите следующий пользовательский JsonConverter
:
public class DataContractDateTimeOffsetConverter : JsonConverter
{
readonly bool canWrite;
public DataContractDateTimeOffsetConverter() : this(true) { }
public DataContractDateTimeOffsetConverter(bool canWrite) => this.canWrite = canWrite;
public override bool CanWrite => canWrite;
public override bool CanConvert(Type objectType) => objectType == typeof(DateTimeOffset) || objectType == typeof(DateTimeOffset?);
[JsonObject(NamingStrategyType = typeof(DefaultNamingStrategy))] // Ignore camel casing
class DateTimeOffsetDTO<TOffset> where TOffset : struct, IComparable, IFormattable
{
public DateTime DateTime { get; set; }
public TOffset OffsetMinutes { get; set; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var input = (DateTimeOffset)value;
var oldDateFormatHandling = writer.DateFormatHandling;
var oldDateTimeZoneHandling = writer.DateTimeZoneHandling;
try
{
writer.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
writer.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
var offsetMinutes = input.Offset.TotalMinutes;
var offsetMinutesInt = checked((int)offsetMinutes);
var dateTime = input.DateTime.AddMinutes(-input.Offset.TotalMinutes);
if (offsetMinutesInt == offsetMinutes) // An integer number of mintues
serializer.Serialize(writer, new DateTimeOffsetDTO<int> { DateTime = dateTime, OffsetMinutes = offsetMinutesInt });
else
serializer.Serialize(writer, new DateTimeOffsetDTO<double> { DateTime = dateTime, OffsetMinutes = offsetMinutes });
}
finally
{
writer.DateFormatHandling = oldDateFormatHandling;
writer.DateTimeZoneHandling = oldDateTimeZoneHandling;
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
switch (reader.MoveToContentAndAssert().TokenType)
{
// note that if there is a possibility of getting ISO 8601 strings for DateTimeOffset as well as complex objects, you may need to configure
// JsonSerializerSettings.DateParseHandling = DateParseHandling.None or DateParseHandling.DateTimeOffset at a higher code level to
// avoid premature deserialization as DateTime by JsonTextReader.
case JsonToken.String:
case JsonToken.Date:
return (DateTimeOffset)JToken.Load(reader);
case JsonToken.StartObject:
var old = reader.DateTimeZoneHandling;
try
{
reader.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
var dto = serializer.Deserialize<DateTimeOffsetDTO<double>>(reader);
var result = new DateTimeOffset(new DateTime(dto.DateTime.AddMinutes(dto.OffsetMinutes).Ticks, DateTimeKind.Unspecified),
TimeSpan.FromMinutes(dto.OffsetMinutes));
return result;
}
finally
{
reader.DateTimeZoneHandling = old;
}
case JsonToken.Null:
return null;
default:
throw new JsonSerializationException(); // Unknown token
}
}
}
public static partial class JsonExtensions
{
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
Теперь вы можете десериализовать JSON, сгенерированный DataContractJsonSerializer
, добавив преобразователь в JsonSerializerSettings.Converters
:
var settings = new JsonSerializerSettings
{
Converters = { new DataContractDateTimeOffsetConverter(true) },
};
var item = JsonConvert.DeserializeObject<TestToSeailize>(json, settings);
Примечания:
Если вы не хотите сериализоваться в формате DataContractJsonSerializer
, передайте canWrite : false
конструктору преобразователя.
Если существует возможность получения строк ISO 8601, а также сложных объектов для значений DateTimeOffset
, вам может потребоваться настроить JsonSerializerSettings.DateParseHandling = DateParseHandling.None
или DateParseHandling.DateTimeOffset
на более высоком уровне кода, чтобы избежать преждевременной десериализации строк ISO 8601 как DateTime
объектов с помощью JsonTextReader
.
Демонстрационная скрипта № 2 здесь.
person
dbc
schedule
01.07.2020
DateTime
иDateTimeOffset
, см. Даты/время и JSON. Также см. соответствующую документацию Newtonsoft newtonsoft.com/json/help/html/DatesInJSON. хтм. - person dbc   schedule 30.06.2020DataContractJsonSerializerSettings.DateTimeFormat
, вы можете получить базовыйDateTime
в формате ISO 8601 следующим образом:{"SaveDate":{"DateTime":"2020-06-04T22:00:00.00+00:00","OffsetMinutes":300}}
. См.: dotnetfiddle.net/tnE2d3. Но, похоже, нет способа сериализоватьDateTimeOffset
как строку ISO 8601. - person dbc   schedule 30.06.2020DateTime
для сериализации вашего свойстваSaveDate
, если вам нужна согласованность между Newtonsoft иDataContractJsonSerializer
. Или вы можете написать конвертер на стороне Newtonsoft. Если бы такой конвертер удовлетворил ваши потребности, я, вероятно, мог бы добавить один ответ. - person dbc   schedule 30.06.2020