c# - 对于没有时区的 DateTimeOffset.MinValue,Json.NET 反序列化 DateTimeOffset 值失败

标签 c# json.net asp.net-core-mvc deserialization datetimeoffset

在我的 ASP.NET Core Web-API 项目中,我收到了对我的一个 API Controller 的 HTTP POST 调用。

在评估 JSON 负载并反序列化其内容时,Json.NET 偶然发现了 0001-01-01T00:00:00 的 DateTime 值,并且无法将其转换为 DateTimeOffset 属性。

我注意到该值应该代表 DateTimeOffset.MinValue 的值,但它缺少时区似乎会使解串器出错。我只能想象 DateTimeOffset.Parse 会尝试将其转换为主机当前时区,这会导致 DateTimeOffset.MinValue 下溢。

这个属性非常简单:

[JsonProperty("revisedDate", NullValueHandling = NullValueHandling.Ignore)]
public DateTimeOffset? RevisedDate { get; set; }

这是发送给客户端的响应:

{
    "resource.revisedDate": [
        "Could not convert string to DateTimeOffset: 0001-01-01T00:00:00. Path 'resource.revisedDate', line 20, position 44."
    ]
}

我正在使用 Newtonsoft.Json v11.0.2,目前处于 UTC + 2(德国)。异常回溯和错误消息在这里:https://pastebin.com/gX9R9wq0 .

我无法修复调用代码,所以我必须在我这边修复它。

但问题是:怎么做?

最佳答案

问题 似乎只有在机器的时区 TimeZoneInfo.Local 时才能重现与 UTC 有正偏移,例如(UTC+01:00) 阿姆斯特丹、柏林、伯尔尼、罗马、斯德哥尔摩、维也纳。我无法在具有非正偏移量的时区重现它,例如 UTC-05:00 或 UTC 本身。

具体来说,在 JsonReader.ReadDateTimeOffsetString()使用 DateTimeStyles.RoundtripKind 调用 DateTimeOffset.TryParse:

if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
{
    SetToken(JsonToken.Date, dt, false);
    return dt;
}

这显然会导致具有正 UTC 偏移量的时区出现下溢错误。如果在调试器中我使用 DateTimeStyles.AssumeUniversal 进行解析,则可以避免该问题。

您可能想要 report an issue关于这个给 Newtonsoft。只有当计算机的时区具有特定值时,特定 DateTimeOffset 字符串的反序列化才会失败这一事实似乎是错误的。

解决方法是使用 IsoDateTimeConverter使用 IsoDateTimeConverter.DateTimeStyles 反序列化您的 DateTimeOffset 属性设置为 DateTimeStyles.AssumeUniversal。此外,有必要通过设置 JsonReader.DateParseHandling = DateParseHandling.None 来禁用 JsonReader 内置的自动 DateTime 识别功能。 ,这必须在阅读器开始解析您的 DateTimeOffset 属性的值之前完成。

首先,定义如下JsonConverter:

public class FixedIsoDateTimeOffsetConverter : IsoDateTimeConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTimeOffset) || objectType == typeof(DateTimeOffset?);
    }

    public FixedIsoDateTimeOffsetConverter() : base() 
    {
        this.DateTimeStyles = DateTimeStyles.AssumeUniversal;
    }
}

现在,如果您可以修改 Controller 的 JsonSerializerSettings,请使用以下设置:

var settings = new JsonSerializerSettings
{
    DateParseHandling = DateParseHandling.None,
    Converters = { new FixedIsoDateTimeOffsetConverter() },
};

如果您不能轻松修改 Controller 的JsonSerializerSettings,则需要从this answer 获取DateParseHandlingConverter How to prevent a single object property from being converted to a DateTime when it is a string 并将其与 FixedIsoDateTimeOffsetConverter 一起应用于您的模型,如下所示:

[JsonConverter(typeof(DateParseHandlingConverter), DateParseHandling.None)]
public class RootObject
{
    [JsonProperty("revisedDate", NullValueHandling = NullValueHandling.Ignore)]
    [JsonConverter(typeof(FixedIsoDateTimeOffsetConverter))]
    public DateTimeOffset? RevisedDate { get; set; }
}

DateParseHandlingConverter 必须应用于模型本身而不是 RevisedDate 属性,因为 JsonReader 已经识别出 0001-01 -01T00:00:00 作为调用 FixedIsoDateTimeOffsetConverter.ReadJson() 之前的 DateTime

更新

comments , @RenéSchindhelm写道,我创建了一个问题让 Newtonsoft 知道。它是 Deserialization of DateTimeOffset value fails depending on system's timezone #1731 .

关于c# - 对于没有时区的 DateTimeOffset.MinValue,Json.NET 反序列化 DateTimeOffset 值失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50628374/

相关文章:

c# - ASP.NET WebAPI 的哪一部分负责将动态解析为 JObject

c# - 无法反序列化当前 JSON 对象(例如 {"name":"value"})

c# - 在 ASP.NET Core 6 MVC 应用程序上创建 SSO(单点登录)CAS

angular - ASP NET CORE - 请求的资源上存在 ANGULAR NO 'Access-Control-Allow-Origin' header

c# - 将 C# 类分离到单独的文件中

c# - 我们如何检查动态粘土对象是否具有属性?

C# - 从 Google Place Api 中提取营业时间

asp.net-core-mvc - 为什么GitHub上的Controller继承自ControllerBase,而VS中却没有?

c# - 检测空间并添加逗号

c# - 正确使用 GLTexImage3D 的问题