【发布时间】:2022-07-12 09:04:47
【问题描述】:
我们将 Asp.Net 应用程序从 .Net Framework 4.7.2 更新到 .Net 5。现在我们在控制器方法中遇到了反序列化 JSON 的问题。在旧版本中,我们使用 Newtonsoft.Json。以前,如果我们在 JSON 中为不可为空的类型(如 int)的属性获得了 null,则反序列化器会采用默认值,或者忽略 null 和错误,并且不会从对象创建中覆盖属性的默认值。但是现在在错误之后整个对象被设置为 null。
{
"effortType": "1",
"cwe": null,
"distanceInKilometers": null,
"effortDate": "2022-03-22T14:45:00+01:00",
"effortInHours": 1.0,
"hours25InHours": null,
"hours50InHours": null,
"hours100InHours": null,
"hours150InHours": null,
"orderNumber": "006001780872",
"withCosts": false,
"isNew": true,
"isEdited": false,
"isDeleted": false
}
public class OrderEffortDto
{
public string EffortType { get; set; }
public bool Cwe { get; set; }
public int? DistanceInKilometers { get; set; }
public DateTimeOffset? EffortDate { get; set; }
public decimal EffortInHours { get; set; }
public decimal Hours25InHours { get; set; }
public decimal Hours50InHours { get; set; }
public decimal Hours100InHours { get; set; }
public decimal Hours150InHours { get; set; }
public string OperationNumber { get; set; }
public bool IsNew { get; set; }
public bool IsEdited { get; set; }
public bool IsDeleted { get; set; }
}
和以前一样 将是一个 OrderEffortDto 实例,其中 Cwe = false 并且所有 HoursXXInHours = 0
我们得到什么 是 OrderEffortDto = null
我们已经尝试在新版本中使用 Newtonsoft,但结果相同。我们还配置了SerializerSettings.NullValueHandling = NullValueHandling.Ignore。这适用于该问题,但对于另一个方向,空值也会被忽略,因为在需要空值的地方将 DTO 序列化为 JSON。
有没有办法恢复旧的行为?是的,在前端修复它以将正确的值放入 JSON 是没有问题的,但是我们的应用程序很大,并且要确定所有我们必须纠正的地方,这很容易出错。
更新 1 为可能有同样问题的人提供
我创建了两个简单的测试项目,一个带有 .Net Framework 4.7.2 的 ASP.Net WebApi 和一个带有 .Net 5 的 ASP.Net WebApi,上面带有 JSON 和 DTO 示例。我从 Newtonsoft 获得了两条类似的带有错误的跟踪,并且已经描述了控制器中 DTO 的结果。 .Net 5 中的 System.Text.Json 也给了我整个 DTO 的空值。
适用于 .Net Framework 4.7.2 的 API
2022-03-24T10:50:05.368 Info Started deserializing WebApplication1NetFramework.Data.OrderEffortDto. Path 'effortType', line 2, position 16.
2022-03-24T10:50:05.388 Error Error deserializing WebApplication1NetFramework.Data.OrderEffortDto. Error converting value {null} to type 'System.Boolean'. Path 'cwe', line 3, position 14.
2022-03-24T10:50:05.403 Error Error deserializing WebApplication1NetFramework.Data.OrderEffortDto. Error converting value {null} to type 'System.Decimal'. Path 'hours25InHours', line 7, position 25.
2022-03-24T10:50:05.403 Error Error deserializing WebApplication1NetFramework.Data.OrderEffortDto. Error converting value {null} to type 'System.Decimal'. Path 'hours50InHours', line 8, position 25.
2022-03-24T10:50:05.403 Error Error deserializing WebApplication1NetFramework.Data.OrderEffortDto. Error converting value {null} to type 'System.Decimal'. Path 'hours100InHours', line 9, position 26.
2022-03-24T10:50:05.404 Error Error deserializing WebApplication1NetFramework.Data.OrderEffortDto. Error converting value {null} to type 'System.Decimal'. Path 'hours150InHours', line 10, position 26.
2022-03-24T10:50:05.404 Verbose Could not find member 'orderNumber' on WebApplication1NetFramework.Data.OrderEffortDto. Path 'orderNumber', line 11, position 17.
2022-03-24T10:50:05.405 Verbose Could not find member 'withCosts' on WebApplication1NetFramework.Data.OrderEffortDto. Path 'withCosts', line 12, position 15.
2022-03-24T10:50:05.407 Info Finished deserializing WebApplication1NetFramework.Data.OrderEffortDto. Path '', line 16, position 1.
2022-03-24T10:50:05.407 Verbose Deserialized JSON:
{
"effortType": "1",
"cwe": null,
"distanceInKilometers": null,
"effortDate": "2022-03-22T14:45:00+01:00",
"effortInHours": 1.0,
"hours25InHours": null,
"hours50InHours": null,
"hours100InHours": null,
"hours150InHours": null,
"orderNumber": "006001780872",
"withCosts": false,
"isNew": true,
"isEdited": false,
"isDeleted": false
}
适用于 .Net 5 的 API
2022-03-24T10:48:19.162 Info Started deserializing WebApplication1NetCore.Data.OrderEffortDto. Path 'effortType', line 2, position 16.
2022-03-24T10:48:19.180 Error Error deserializing WebApplication1NetCore.Data.OrderEffortDto. Error converting value {null} to type 'System.Boolean'. Path 'cwe', line 3, position 14.
2022-03-24T10:48:19.196 Error Error deserializing WebApplication1NetCore.Data.OrderEffortDto. Error converting value {null} to type 'System.Decimal'. Path 'hours25InHours', line 7, position 25.
2022-03-24T10:48:19.196 Error Error deserializing WebApplication1NetCore.Data.OrderEffortDto. Error converting value {null} to type 'System.Decimal'. Path 'hours50InHours', line 8, position 25.
2022-03-24T10:48:19.197 Error Error deserializing WebApplication1NetCore.Data.OrderEffortDto. Error converting value {null} to type 'System.Decimal'. Path 'hours100InHours', line 9, position 26.
2022-03-24T10:48:19.197 Error Error deserializing WebApplication1NetCore.Data.OrderEffortDto. Error converting value {null} to type 'System.Decimal'. Path 'hours150InHours', line 10, position 26.
2022-03-24T10:48:19.197 Verbose Could not find member 'orderNumber' on WebApplication1NetCore.Data.OrderEffortDto. Path 'orderNumber', line 11, position 17.
2022-03-24T10:48:19.197 Verbose Could not find member 'withCosts' on WebApplication1NetCore.Data.OrderEffortDto. Path 'withCosts', line 12, position 15.
2022-03-24T10:48:19.199 Info Finished deserializing WebApplication1NetCore.Data.OrderEffortDto. Path '', line 16, position 1.
2022-03-24T10:48:19.200 Verbose Deserialized JSON:
{
"effortType": "1",
"cwe": null,
"distanceInKilometers": null,
"effortDate": "2022-03-22T14:45:00+01:00",
"effortInHours": 1.0,
"hours25InHours": null,
"hours50InHours": null,
"hours100InHours": null,
"hours150InHours": null,
"orderNumber": "006001780872",
"withCosts": false,
"isNew": true,
"isEdited": false,
"isDeleted": false
}
感谢 @dbc 的 cmets。我将在提到的帖子Json.net deserialization null guid case 中尝试使用转换器,但也会记录出现的情况以修复根本原因。
更新 2
我稍微改动了转换器并使用了“SwaggerGen.TypeExtensions.GetDefaultValue()”。所以我能够删除泛型并为所有不可为空的类型使用一个转换器。
public class NullToDefaultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
var defaultValue = objectType.GetDefaultValue();
return defaultValue != null;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
if (token.Type == JTokenType.Null)
// here I will add a logger to get all faulty calls
return objectType.GetDefaultValue();
return token.ToObject(objectType); // Deserialize using default serializer
}
// Return false I don't want default values to be written as null
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
【问题讨论】:
-
@das.fliagsi 你能在可以为 null 的属性顶部尝试 [JsonProperty] 看看它是否正确反序列化
-
不幸的是不起作用。它可以与 [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 一起使用,但我又遇到了另一个方向的问题。
-
在 .NET 5 中,您是否已从 Json.NET 切换到 System.Text.Json?顺便说一句,我认为 Json.NET 不会自动将
null值反序列化为不可为空的值类型的默认值;你需要一个转换器。参见例如Json.net deserialization null guid case. -
@dbc 是的,我也尝试使用 System.Text.Json。在那里,我还得到了整个对象的空对象。 NullValueHandling 和 DefaultValueHandling 的设置非常相似。 Json.Net 的一个好处是 TraceWriter。正因为如此,我同意你可能不会发生转变。因为此跟踪在反序列化非可空类型的空值时显示错误。但是这个错误被忽略并且属性被跳过。因此,由于 OrderEffortDto 对象的正常创建,不可为空的属性已经填充了默认值。
-
@das.flaigsi - 如果 Json.NET 总是产生错误,但在过去,asp.net 会在基于每个属性的模型绑定期间忽略它们,但现在它们会导致模型绑定完全失败,而不是试图恢复旧的行为,我会修复错误。将
NullToDefaultConverter<T>从 Json.net deserialization null guid case 添加到所有必要的T到JsonSerializerSettings.Converters应该可以完成这项工作。事实上,我可能会建议将其作为副本关闭,同意吗?
标签: asp.net json asp.net-core json.net