我有一个具有以下属性的对象(自1970年以来,它具有毫秒数):
public double AsOfDate { get; set; }
当我序列化它(在ASP.NETMVC4应用程序中)时,它很好。然而,当我稍后将JSON对象发送回服务器时,我得到了反序列化错误:
读取日期时出错。意外的标记:Integer。路径"performance.asOfDate",第1行,位置95。
和堆栈跟踪:
位于Newtonsoft.Json.JsonReader.ReadAsDateTimeInternal()位于的Newtonsoft.Json.JsonTextReader.ReadAsDateTime()Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(JsonReaderreader,JsonContract合约,Boolean hasConverter)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolvePropertyAndConstructorValues(JsonObjectContract合约,JsonProperty容器属性,JsonReader阅读器,类型objectType)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObjectFromNonDefaultConstructor(JsonReader阅读器,JsonObjectContract合同,JsonProperty容器Property,ConstructorInfo ConstructorInfo,字符串id)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReaderreader,JsonObjectContract objectContract,JsonPropertycontainerMember,JsonProperty containerProperty,字符串id,Boolean&createdFromNonDefaultConstructor)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReaderreader,Type objectType,JsonContract合约,JsonProperty成员,JsonContainerContract containerContract,JsonProperty containerMember,对象existingValue)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReaderreader,Type objectType,JsonContract合约,JsonProperty成员,JsonContainerContract containerContract,JsonProperty containerMember,对象existingValue)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolvePropertyAndConstructorValues(JsonObjectContract合约,JsonProperty容器属性,JsonReader阅读器,类型objectType)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObjectFromNonDefaultConstructor(JsonReader阅读器,JsonObjectContract合同,JsonProperty容器Property,ConstructorInfo ConstructorInfo,字符串id)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(JsonReaderreader,JsonObjectContract objectContract,JsonPropertycontainerMember,JsonProperty containerProperty,字符串id,Boolean&createdFromNonDefaultConstructor)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReaderreader,Type objectType,JsonContract合约,JsonProperty成员,JsonContainerContract containerContract,JsonProperty containerMember,对象existingValue)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReaderreader,Type objectType,JsonContract合约,JsonProperty成员,JsonContainerContract containerContract,JsonProperty containerMember,对象existingValue)Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader阅读器,类型对象类型,布尔检查AdditionalContent)
所以,在反序列化阶段,JSON库试图将asOfDate解析为DateTime,只是因为它的名称中有Date字符串?有办法解决这个问题吗?以下是请求正文示例:
{
"performance":{
"performanceSinceInception":0.76,
"performanceMtd":0.41,
"asOf":1382486400000,
"inceptionDate":1359676800000,
"monthPerformanceStart":1380585600000
},
"rating":1,
"isActive":false,
"effectiveFrom":1359676800000,
"effectiveTo":1383919343000,
"globalValidationFlag":true,
"whitelistValidationFlag":false,
"canBeReactivated":false,
"canBeRemoved":false,
"belongsToMainBucket":true
}
我已经测试过了,当我将属性名称从asOfDate更改为asOf时,它就可以工作了。所以我谴责基于约定的反序列化。有没有办法推翻它?
我认为问题在于您的类没有无参数(默认)构造函数。(我可以判断是这种情况,因为在您发布的堆栈跟踪中,它显示CreateObjectFromNonDefaultConstructor
正在被调用。)
当一个类没有默认构造函数时,Json.Net会做一些奇怪的事情,试图弄清楚它可以使用哪些构造函数来创建对象,以及如何将构造函数参数与Json数据匹配以调用它。这需要一定的猜测,而且并不总是有效的。
一种特别的情况是,如果您有一个构造函数,其参数名称与JSON数据中的属性匹配,但数据类型不兼容,那么它将无法正常工作。例如,考虑以下程序:
class Program
{
static void Main(string[] args)
{
string json = @"{ ""asOf"" : 1382486400000 }";
var obj = JsonConvert.DeserializeObject<Performance>(json);
Console.WriteLine(obj.asOf);
}
public class Performance
{
public Performance(DateTime asOf)
{
this.asOf = asOf.Ticks;
}
public double asOf { get; set; }
}
}
此程序将失败,错误与您的问题相同。没有默认的构造函数,所以Json.Net尝试使用那里的构造函数。构造函数中有一个参数asOf
,恰好数据中也有一个属性asOf
。所以它试图用它来构造对象。但是,这不会起作用,因为数据是一个整数,Json.Net不知道如何将其转换为日期。
有几种可能的解决方案。最好的解决方案是简单地添加一个默认构造函数。如果有一个公共的默认构造函数,Json.Net总是更喜欢它而不是其他构造函数。通过此更改,Json.Net将使用默认构造函数来创建对象,然后将Json数据中的asOf
属性与对象中的asOf
属性相匹配,后者的类型兼容。所以这很好用。
另一种可能的解决方案是更改构造函数参数的名称,使其与JSON数据不匹配(或者反过来,更改类和JSON数据中对象属性的名称,使得它们与构造函数不匹配)。在这种情况下,Json.Net被迫使用默认值传递给构造函数来创建对象。然后,它将把JSON数据与任何剩余的对象属性进行匹配。对于我们的例子,这也是有效的。当然,依赖这种行为有点风险,因为Json.Net不知道构造函数中可能有什么逻辑。例如,您可能有一个代码,它拒绝默认值(例如null)对某些参数无效。在这种情况下,Json.Net将无法构造该对象。它只能用你的付出做到最好,而这可能并不总是如预期的那样奏效。
第三种解决方案是为对象创建一个自定义JsonConverter
,并让Json.Net使用它来构造和填充对象。如果你有一个复杂的对象,它不能有默认的构造函数,但需要特殊的处理来填充它,这是你最好的选择,而不是依赖Json.Net来尝试自己解决它。当然,这需要更多的代码,但制作转换器并不太难。如果你需要的话,我可以提供一个例子。