Json.Net 无法反序列化 DateTime.MinValue。

5
我尝试了各种可能的方式将包含 DateTime.MinValue 的 Json 字符串反序列化,但当调用我的对象的 set 方法时,日期总是从 -01-01-01- 改变为 -01-01-02-。
被解析的 Json 明显包含
"inception_date": "0001-01-01T00:00:00+00:00"

然后我对它调用 JsonConvert

return JsonConvert.DeserializeObject<T>(json, deserializerSettings);

这里的 T 是一个基础结构(struct),它包含一个属性: DateTime inception_date { get; set; }。反序列化设置如下:

deserializerSettings = new JsonSerializerSettings()
{
    DateFormatHandling = DateFormatHandling.IsoDateFormat,
    DateParseHandling = Newtonsoft.Json.DateParseHandling.None,
    DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc
};

然而在 Newtonsoft.Json.dll 的深处,上述时间会被转换为以下的 jObject

"inception_date": "0001-01-02T00:00:00Z"

我无法进入他们的代码,但在我的代码看到另一个调用之前,已经造成了损坏。JsonConverter中的ReadJson调用serializer.Populate(jObject.CreateReader(), target);,其中target是我的类T的一个实例,而jObject以某种方式呈现了上述不正确的日期。
有人能想出这是为什么或者如何防止它吗?jObject似乎是以一种忽略我的序列化程序设置的方式创建的,明确表示不要干扰日期字符串(DateParseHandling.None)。
我已经拍了屏幕截图,以说明Newtonsoft的JsonConvert方法似乎已经丢失了一个重要的配置值。
正如您所看到的,在代码中我调用JsonConvert的这一点:

enter image description here

dateParseHandling的值设置为None,这是使其正常工作所必需的。

在下一步中,我跳过了几个内部Newtonsoft调用,并着陆在一个通用的JsonConverter实现中,该实现是从一个已接受的参考实现中借来的,以便能够看到发生了什么。传入的JsonReader突然失去了dateParseHandling的值:

enter image description here

由于这个值被切换回DateTime - jObject的内部工作方式试图将其表示为内部本地化的DateTime,由于我的时区是负数并且我们已经表示了最小的DateTime值,所以会下溢,导致转换回UTC会添加一整天。

类似于您之前的问题(JObject.Parse),为什么要使用serializer.Populate?使用适当的设置JsonConvert.SerializeObject。这都与如何使用“瑞士军刀”有关。 - L.B
根据Json.Net的文档,对于您提供的示例日期时间,您不需要任何序列化器设置。您尝试过不使用任何额外设置吗? - Pete Garafano
@PeteGarafano 是的,文档确实有误导性。如果您不使用任何设置,它将默认使用您的语言环境。 - Alain
@L.B 正如您在我的第一张截图中所看到的,我肯定是使用了适当的设置JsonConvert.SerializeObject,但它将错误的日期放入了我的对象中。我引入了JsonCreationConverter<T>上线的JsonConverter覆盖,以便我可以添加一个更深层次的断点并查看发生了什么。 - Alain
@Alain 抱歉,但是带有图片的问题不太容易阅读。 - L.B
1个回答

3

尝试:

deserializerSettings = new JsonSerializerSettings()
{
    DateFormatHandling = DateFormatHandling.IsoDateFormat,
    DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset
}

这导致我得到了1/1/0001 12:00:00 AM而不是1/2/0001 12:00:00 AM
以下是我的测试代码(在LINQPad中编写)。
void Main()
{
    var deserializerSettings = new JsonSerializerSettings()
    {
        DateFormatHandling = DateFormatHandling.IsoDateFormat,
        DateParseHandling = Newtonsoft.Json.DateParseHandling.None,
        DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc
    };
    var json = "{\"inception_date\": \"0001-01-01T00:00:00+00:00\"}";
    var parsedObj = JsonConvert.DeserializeObject<TestClass>(json, deserializerSettings);
    Console.WriteLine(parsedObj);

    deserializerSettings = new JsonSerializerSettings()
    {
        DateFormatHandling = DateFormatHandling.IsoDateFormat,
        DateParseHandling = Newtonsoft.Json.DateParseHandling.DateTimeOffset
    };
    parsedObj = JsonConvert.DeserializeObject<TestClass>(json, deserializerSettings);
    Console.WriteLine(parsedObj);
}

public class TestClass
{
    public DateTime inception_date {get;set;}
}

输出结果:

程序输出


这看起来很有前途,但我很惊讶它为什么能在你那里工作而不是在我的那里。我传递的是完全相同的JSON:"{\"expiry_date\": \"0001-01-01T00:00:00+00:00\"}" 但是使用那些配置选项(没有其他选项),我会得到一个异常 InvalidCastException at Newtonsoft.Json.JsonReader.ReadAsDateTimeInternal() in c:\Dev\Releases\Working\Newtonsoft.Json\Src\Newtonsoft.Json\JsonReader.cs:line 613. 我想知道我的JSON.Net版本是否存在漏洞?(V4.5.5.14908) - Alain
哇,他们已经在使用V6了。我完全不知道我落后了这么多——我以为几个月前我已经拥有他们的最新版本了。至少现在我可以放心地睡觉了,因为我有了一个可能的解决方案。谢谢你,如果这个方法有效,我会给你加分的! - Alain
.Net 是 4.0 版本。Culture 字符串为 "en-CA",当前时区偏移为 -04:00。 - Alain
我似乎无法复制您的问题,即使在.NET 4中也是如此。我尝试使用SetCulture属性通过NUnit测试运行它,但结果相同。只是为了进行一次健全性检查,您能否发布您的POCO,T? - Pete Garafano
我稍微修改了你上面的例子,以便复现错误:http://pastebin.com/WRPbz2JS。看起来可能是自从Json.Net发布以来广泛使用的这个JsonCreationConverter有问题。 - Alain
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接