Json.NET对没有时区的DateTimeOffset.MinValue进行反序列化DateTimeOffset值失败

Ren*_*elm 5 c# datetimeoffset json.net deserialization asp.net-core-mvc

在我的ASP.NET Core Web-API项目中,我正在对我的一个API控制器进行HTTP POST调用。

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

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

该属性非常简单:

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

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

{
    "resource.revisedDate": [
        "Could not convert string to DateTimeOffset: 0001-01-01T00:00:00. Path 'resource.revisedDate', line 20, position 44."
    ]
}
Run Code Online (Sandbox Code Playgroud)

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

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

但是问题是:如何?

Joh*_*ais 6

这是我用来解决 .NET Core 3 中的问题的方法。

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
            .AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore;
                options.SerializerSettings.DateParseHandling = DateParseHandling.None;
                options.SerializerSettings.Converters.Add(new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal });
            });
...
Run Code Online (Sandbox Code Playgroud)


dbc*_*dbc 5

仅当机器的时区TimeZoneInfo.Local与UTC的偏差为正时,该问题才可重现(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna。我无法在带有非正偏移量的时区(例如UTC-05:00UTC本身)中重现它。

具体而言,在JsonReader.ReadDateTimeOffsetString()呼叫中DateTimeOffset.TryParse使用DateTimeStyles.RoundtripKind

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

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

您可能需要向Newtonsoft 报告有关此问题DateTimeOffset仅当计算机的时区具有特定值时,特定字符串的反序列化才会失败的事实似乎是错误的。

解决方法是使用设置为IsoDateTimeConverter来反序列化DateTimeOffset属性。此外,有必要禁用自动内置到识别的设置,必须做之前,读者开始解析你的价值属性。IsoDateTimeConverter.DateTimeStylesDateTimeStyles.AssumeUniversalDateTimeJsonReaderJsonReader.DateParseHandling = DateParseHandling.NoneDateTimeOffset

首先,定义以下内容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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果可以JsonSerializerSettings为控制器修改,请使用以下设置:

var settings = new JsonSerializerSettings
{
    DateParseHandling = DateParseHandling.None,
    Converters = { new FixedIsoDateTimeOffsetConverter() },
};
Run Code Online (Sandbox Code Playgroud)

如果你不能轻易修改你的控制器JsonSerializerSettings,你需要抢DateParseHandlingConverter这个答案如何防止被转换为当它是一个字符串,一个DateTime一个对象的属性,并将其应用以及FixedIsoDateTimeOffsetConverter进行如下模型:

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

DateParseHandlingConverter必须应用于模型本身而不是RevisedDate属性,因为在进行调用之前,JsonReader将已被识别0001-01-01T00:00:00为。DateTimeFixedIsoDateTimeOffsetConverter.ReadJson()

更新资料

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

  • 解决方法有效。我[创建了一个问题](https://github.com/JamesNK/Newtonsoft.Json/issues/1731) 让 Newtonsoft 知道。也许他们想出了解决办法。我真诚地感谢 dbc 在这里提供的优质帮助。+1 (2认同)