Json.NET DateTimeOffset.MinValue 反序列化 DateTimeOffset 值失败,没有时区
Json.NET deserializing DateTimeOffset value fails for DateTimeOffset.MinValue without timezone
在我的 ASP.NET Core Web-API 项目中,我收到一个 HTTP POST 调用到我的 API 控制器之一。
在评估 JSON 负载并反序列化其内容时,Json.NET 偶然发现日期时间值 0001-01-01T00:00:00
并且无法将其转换为 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.
我无法修复调用代码,所以我必须在我这边修复它。
但问题是:如何?
检查您的 Json.NET 版本,然后检查您的输入值和格式。我正在尝试以下示例,它对我来说工作正常:
void Main()
{
var json = @"{""offset"":""0001-01-01T00:00:00""}";
var ds = Newtonsoft.Json.JsonConvert.DeserializeObject<TestDS>(json);
Console.WriteLine(ds);
}
public class TestDS {
[Newtonsoft.Json.JsonProperty("offset", NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public DateTimeOffset? DSOffset { get; set; }
}
这是输出:
DSOffset 1/1/0001 12:00:00 AM -06:00
问题 似乎只有当机器的时区 TimeZoneInfo.Local
与 UTC 有正偏移时才可重现,例如(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
。我无法在具有非正偏移量的时区重现它,例如 UTC-05:00
或 UTC 本身。
具体来说,在 JsonReader.ReadDateTimeOffsetString()
中调用 DateTimeOffset.TryParse
使用 DateTimeStyles.RoundtripKind
:
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
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
,必须在 reader 开始解析之前完成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;
}
}
现在,如果您可以为您的控制器修改 JsonSerializerSettings
,请使用以下设置:
var settings = new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.None,
Converters = { new FixedIsoDateTimeOffsetConverter() },
};
如果您无法轻松修改控制器的 JsonSerializerSettings
,则需要从 to 中获取 DateParseHandlingConverter
并将其与 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
识别为 DateTime
在调用 FixedIsoDateTimeOffsetConverter.ReadJson()
之前。
更新
在, @RenéSchindhelm writes, I created an issue to let Newtonsoft know. It is Deserialization of DateTimeOffset value fails depending on system's timezone #1731.
这就是我用来解决 .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 });
});
...
将DateTimeOffset
更改为DateTime
解决了问题。
在我的 ASP.NET Core Web-API 项目中,我收到一个 HTTP POST 调用到我的 API 控制器之一。
在评估 JSON 负载并反序列化其内容时,Json.NET 偶然发现日期时间值 0001-01-01T00:00:00
并且无法将其转换为 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.
我无法修复调用代码,所以我必须在我这边修复它。
但问题是:如何?
检查您的 Json.NET 版本,然后检查您的输入值和格式。我正在尝试以下示例,它对我来说工作正常:
void Main()
{
var json = @"{""offset"":""0001-01-01T00:00:00""}";
var ds = Newtonsoft.Json.JsonConvert.DeserializeObject<TestDS>(json);
Console.WriteLine(ds);
}
public class TestDS {
[Newtonsoft.Json.JsonProperty("offset", NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public DateTimeOffset? DSOffset { get; set; }
}
这是输出:
DSOffset 1/1/0001 12:00:00 AM -06:00
问题 似乎只有当机器的时区 TimeZoneInfo.Local
与 UTC 有正偏移时才可重现,例如(UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
。我无法在具有非正偏移量的时区重现它,例如 UTC-05:00
或 UTC 本身。
具体来说,在 JsonReader.ReadDateTimeOffsetString()
中调用 DateTimeOffset.TryParse
使用 DateTimeStyles.RoundtripKind
:
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
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
,必须在 reader 开始解析之前完成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;
}
}
现在,如果您可以为您的控制器修改 JsonSerializerSettings
,请使用以下设置:
var settings = new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.None,
Converters = { new FixedIsoDateTimeOffsetConverter() },
};
如果您无法轻松修改控制器的 JsonSerializerSettings
,则需要从 DateParseHandlingConverter
并将其与 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
识别为 DateTime
在调用 FixedIsoDateTimeOffsetConverter.ReadJson()
之前。
更新
在
这就是我用来解决 .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 });
});
...
将DateTimeOffset
更改为DateTime
解决了问题。