JsonSerializer:区分DateTime对象的种类

时间:2019-05-02 20:02:31

标签: c# jsonserializer

我有一个动态对象,其中包含日期(以及其他数据)。

其中一些日期具有UTC类型,其他日期具有本地类型,例如:

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
}

这意味着我只想在DateTime的类型为Utc时添加一个“ Z”

使用IsoDateTimeConverter和/或JsonSerializerSettings可能吗?

2 个答案:

答案 0 :(得分:2)

我将使用IsoDateTimeConverter而不设置DateTimeFormat,它会输出完整的DateTime值(以秒为单位),如果DateTimeKindUTC,如果是Local,则为“ + -HH:mm”格式的时区偏移量;如果None,则为空字符串。

否则,K specifier将根据需要设置值的格式,并为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格式允许日期和时间的多种变化,达到任意精度。来自Wikipedia's article on ISO 8601

  

小数部分的小数位数没有限制。但是,小数位数需要经过通信各方的同意。

答案 1 :(得分:1)

几件事:

  • 您不应该将DateTime的值与DateTimeKind.Local的值进行序列化,不要偏移。由于是当地时间,因此应提供当地时间。否则,接收方可能会在本地时间 中解释该值-可能与发送方的本地时间不同。

  • The K specifierDateTimeStyles.RoundTripKind配对时,可以正确地序列化所有受支持的ISO 8601格式:

    • DateTimeKind.Utc被序列化并附加了Z
    • DateTimeKind.Local被序列化并附加了本地偏移量,例如-07:00
    • DateTimeKind.Unspecified被序列化而未附加任何内容
  • 这些是IsoDateTimeConverter已经给出的默认值,DateTimeRoundTripKind值的默认转换器。因此,在大多数情况下,您根本不需要指定任何转换器。

  • ISO 8601允许任意长度的分数秒。DateTime样式给出7个小数,因为这是isoDateTimeConverter.DateTimeFormat = "yyyy-MM-dd'T'HH:mm:ssK"; 支持的精度。 JavaScript中的客户端代码通常仅支持毫秒,因此在客户端上解析时多余的小数将被截断。

  • 如果必须截断小数,则:

    DateTimeKind.Unspecified
  • 如果您觉得必须截断本地时间的偏移量,更好的方法是在序列化之前使用DateTime.SpecifyKind设置JsonConverter

  • 如果您想全局更改此 ,则可以创建自己的IsoDateTimeConverter。您可以继承ReadJson并覆盖WriteJsonJsonConverter方法,也可以直接从var now = DateTime.Now; var nums = new List<Num> { new Num { Min = now, Max = now.AddDays(1) }, new Num { Min = now.AddDays(10), Max = now.AddDays(50) }, new Num { Min = now.AddDays(51), Max = now.AddDays(100) }, }; var minMaxNum = nums.Select(num => new Num { Min = nums.Min(_ => _.Min), Max = nums.Max(_ => _.Max), }).First(); 开始。由于我不推荐这种方法,因此我不会在此处添加一个。