【问题标题】:JsonSerializer: discriminate the Kind of DateTime objectsJsonSerializer:区分 DateTime 对象的种类
【发布时间】:2019-05-02 20:02:31
【问题描述】:

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

其中一些日期具有 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 可以做到这一点吗?

【问题讨论】:

标签: c# jsonserializer


【解决方案1】:

如果DateTimeKindUTC 或时区偏移量,我会使用IsoDateTimeConverter 而不设置DateTimeFormat,它会输出完整的DateTime 值(以秒为单位)和“Z”如果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

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

【讨论】:

    【解决方案2】:

    一些事情:

    • 您不应该使用没有偏移量的 DateTimeKind.Local 值序列化 DateTime 值。由于是本地时间,因此应提供本地偏移量。否则接收方可能会以他们的当地时间解释该值 - 这可能与发送方的不同。

    • The K specifierDateTimeStyles.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";
      
    • 如果您觉得必须截断本地时间的偏移量,更好的方法是在序列化之前使用DateTime.SpecifyKind 设置DateTimeKind.Unspecified

    • 如果您觉得应该全局地更改此设置,那么您可以创建自己的JsonConverter。您可以从IsoDateTimeConverter 继承并覆盖ReadJsonWriteJson 方法,或者您可以直接从JsonConverter 开始。由于不推荐这种方式,这里就不加了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多