不带时区的DateTimeOffset.MinValue的Json.NET反序列化DateTimeOffset值失败 [英] Json.NET deserializing DateTimeOffset value fails for DateTimeOffset.MinValue without timezone

查看:122
本文介绍了不带时区的DateTimeOffset.MinValue的Json.NET反序列化DateTimeOffset值失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

我注意到该值大概应该代表DateTimeOffset.MinValue的值,但是缺少时区似乎会使解串器崩溃.我只能想象DateTimeOffset.Parse试图将其转换为主机的当前时区,从而导致DateTimeOffset.MinValue的下溢.

该属性非常简单:

  [JsonProperty("revisedDate",NullValueHandling = NullValueHandling.Ignore)]公共DateTimeOffset?RevisedDate {get;放;} 

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

  {"resource.revisedDate":[无法将字符串转换为DateTimeOffset:0001-01-01T00:00:00.路径'resource.revisedDate',第20行,位置44."]} 

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

我无法修复调用代码,因此必须在行的一侧进行修复.

但问题是:如何?

解决方案

仅当计算机的时区 JsonReader.ReadDateTimeOffsetString() 使用 DateTimeStyles.RoundtripKind 调用 DateTimeOffset.TryParse :

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

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

您可能想向Newtonsoft报告此问题.仅当计算机的时区具有某些值时,对特定的 DateTimeOffset 字符串进行反序列化的事实似乎是错误的.

解决方法是使用 IsoDateTimeConverter 使用DateTimeOffset 属性> IsoDateTimeConverter.DateTimeStyles 设置为 DateTimeStyles.AssumeUniversal .此外,有必要通过设置 的单个答案属性,如果它是字符串,则如何防止将其转换为DateTime 并将其以及 FixedIsoDateTimeOffsetConverter 应用于模型,如下所示:

  [JsonConverter(typeof(DateParseHandlingConverter),DateParseHandling.None)]公共类RootObject{[JsonProperty("revisedDate",NullValueHandling = NullValueHandling.Ignore)][JsonConverter(typeof(FixedIsoDateTimeOffsetConverter))]公共DateTimeOffset?RevisedDate {get;放;}} 

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

更新

评论 @RenéSchindhelm写道,我创建了一个让Newtonsoft知道的问题.它是 对DateTimeOffset值的反序列化失败取决于系统时区#1731 .

In my ASP.NET Core Web-API project, I'm getting a HTTP POST call to one of my API controllers.

While evaluating the JSON payload and deserializing its contents, Json.NET stumbles upon a DateTime value of 0001-01-01T00:00:00 and can't convert it to a DateTimeOffset property.

I notice that the value propably should represent the value of DateTimeOffset.MinValue, but its lack of a timezone seems to trip the deserializer up. I can only imagine that the DateTimeOffset.Parse tries to translate it to the hosts current timezone, which results in an underflow of DateTimeOffset.MinValue.

The property is pretty simplistic:

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

And here is the response sent to the client:

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

I'm using Newtonsoft.Json v11.0.2 and currently am in UTC + 2 (Germany). The exception traceback and error message are here: https://pastebin.com/gX9R9wq0.

I can't fix the calling code, so I have to fix it on my side of the line.

But the question is: How?

解决方案

The problem seems reproducible only when the machine's time zone TimeZoneInfo.Local has a positive offset from UTC, e.g. (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna. I was unable to reproduce it in time zones with a non-positive offset such as UTC-05:00 or UTC itself.

Specifically, in JsonReader.ReadDateTimeOffsetString() a call is made to DateTimeOffset.TryParse using DateTimeStyles.RoundtripKind:

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

This apparently causes an underflow error in time zones with a positive UTC offset. If in the debugger I parse using DateTimeStyles.AssumeUniversal instead, the problem is avoided.

You might want to report an issue about this to Newtonsoft. The fact that deserialization of a specific DateTimeOffset string fails only when the computer's time zone has certain values seems wrong.

The workaround is to use IsoDateTimeConverter to deserialize your DateTimeOffset properties with IsoDateTimeConverter.DateTimeStyles set to DateTimeStyles.AssumeUniversal. In addition it is necessary to disable the automatic DateTime recognition built into JsonReader by setting JsonReader.DateParseHandling = DateParseHandling.None, which must be done before the reader begins to parse the value for your DateTimeOffset properties.

First, define the following 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;
    }
}

Now, if you can modify the JsonSerializerSettings for your controller, use the following settings:

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

If you cannot easily modify your controller's JsonSerializerSettings you will need to grab DateParseHandlingConverter from this answer to How to prevent a single object property from being converted to a DateTime when it is a string and apply it as well as FixedIsoDateTimeOffsetConverter to your model as follows:

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

DateParseHandlingConverter must be applied to the model itself rather than the RevisedDate property because the JsonReader will already have recognized 0001-01-01T00:00:00 as a DateTime before the call to FixedIsoDateTimeOffsetConverter.ReadJson() is made.

Update

In comments, @RenéSchindhelm writes, I created an issue to let Newtonsoft know. It is Deserialization of DateTimeOffset value fails depending on system's timezone #1731.

这篇关于不带时区的DateTimeOffset.MinValue的Json.NET反序列化DateTimeOffset值失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆