JsonSerializer: различать объекты типа DateTime - PullRequest
0 голосов
/ 02 мая 2019

У меня есть динамический объект, который содержит даты (среди прочих данных).

Некоторые из этих дат имеют Kind UTC, а другие имеют Kind Local, например:

var dynamicObject = new 
{
     utcDate = DateTime.UtcNow,    //This one has Kind = DateTimeKind.Utc
     localDate = DateTime.Now      //This one has Kind = DateTimeKind.Local
}

И затем яиметь JsonSerializer, который работает следующим образом:

var isoDateTimeConverter = new IsoDateTimeConverter();
isoDateTimeConverter.DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'";
                                            //                       ^
                                            //                  Notice this

var serializerSettings = new JsonSerializerSettings();
SerializerSettings.Converters.Add(isoDateTimeConverter);

var response = context.HttpContext.Response;
var writer = new JsonTextWriter(response.Output) { Formatting = Formatting };

var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer, dynamicObject);

writer.Flush();

, который создает строку JSON следующим образом:

{
     "utcDate":"2019-05-02T19:52:20Z",
     "localDate":"2019-05-02T15:52:20Z"
}

Это ожидается из-за того, как определен мой isoDateTimeConverter.

Но я хотел бы сериализовать следующим образом:

{
     "utcDate":"2019-05-02T19:52:20Z",
     "localDate":"2019-05-02T15:52:20"    // <--- no Z
}

То есть я хотел бы добавить 'Z' только тогда, когда тип DateTime равен Utc .

Возможно ли это с IsoDateTimeConverter и / или JsonSerializerSettings?

Ответы [ 2 ]

2 голосов
/ 02 мая 2019

Я бы либо использовал IsoDateTimeConverter без установки DateTimeFormat, который выводит полное значение DateTime, с долями секунд, и "Z", если DateTimeKind равно UTC или смещение часового пояса в формате "+ -HH: мм", если Local (и пустая строка, если None).

В противном случае спецификатор K отформатирует значение по вашему желанию, добавив те же значения для смещения от UTC.

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

public class Program
{
    public static void Main(string[] args)
    {
        var dynamicObject = new
        {
            utcDate = DateTime.UtcNow, //This one has Kind = DateTimeKind.Utc
            localDate = DateTime.Now //This one has Kind = DateTimeKind.Local
        }

        ;
        var isoDateTimeConverter = new IsoDateTimeConverter();
        isoDateTimeConverter.DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssK";
        var serializerSettings = new JsonSerializerSettings();
        serializerSettings.Converters.Add(isoDateTimeConverter);
        var s = new System.Text.StringBuilder();
        using (var w = new System.IO.StringWriter(s))
        using (var writer = new JsonTextWriter(w)
                   {Formatting = Formatting.Indented})
        {
            var serializer = JsonSerializer.Create(serializerSettings);
            serializer.Serialize(writer, dynamicObject);
            writer.Flush();
        }

        Console.WriteLine(s.ToString());
    }
}

dotnetfiddle .

Формат ISO 8601 допускает множество вариаций даты и времени с произвольной точностью. Из статьи Википедии о ISO 8601 :

Нет ограничения на количество десятичных разрядов для десятичной дроби. Тем не менее, число десятичных знаков должно быть согласовано между сообщающими сторонами.

1 голос
/ 02 мая 2019

Несколько вещей:

  • Не следует сериализовать значения DateTime со значениями DateTimeKind.Local без смещения. Поскольку это местное время, необходимо указать местное смещение. В противном случае получатель может интерпретировать значение в их местном времени - которое может отличаться от значения отправителя.

  • Спецификатор K в сочетании с DateTimeStyles.RoundTripKind правильно сериализует во всех поддерживаемых форматах ISO 8601:

    • DateTimeKind.Utc сериализуется с добавлением Z
    • DateTimeKind.Local сериализуется с добавлением локального смещения, такого как -07:00
    • DateTimeKind.Unspecified сериализуется без добавления чего-либо
  • Это значения по умолчанию, уже заданные IsoDateTimeConverter, который является преобразователем по умолчанию для значений DateTime. Таким образом, в большинстве случаев вам вообще не нужно указывать какой-либо конвертер.

  • ISO 8601 допускает доли секунды до любой длины. Стиль RoundTripKind дает 7 десятичных знаков, потому что это точность, поддерживаемая DateTime. Код на стороне клиента в JavaScript, как правило, поддерживает только миллисекунды, поэтому при анализе на клиенте дополнительные десятичные числа будут усечены.

  • Если вы должны усечь десятичные дроби, то:

    isoDateTimeConverter.DateTimeFormat = "yyyy-MM-dd'T'HH:mm:ssK";
    
  • Если вы чувствуете, что должны обрезать смещения для местного времени, лучшим способом было бы установить DateTimeKind.Unspecified с использованием DateTime.SpecifyKind до сериализации.

  • Если вы чувствуете, что должны изменить это глобально , тогда вы создадите свой собственный JsonConverter. Вы можете наследовать от IsoDateTimeConverter и переопределять методы ReadJson и WriteJson, или вы можете просто начать прямо с JsonConverter. Поскольку я не рекомендую этот подход, я не буду добавлять его здесь.

...