简单地说,我有一个简单的 ASP.NET Core 3.1 MVC 应用程序,该应用程序的模型在其中一个属性上具有自定义 JsonConverter 属性。如果 View 具有 @Json.Serialize 仅将属性作为输入,则不会调用自定义 JsonConverter。
我的 View /Home/Index.cshtml View :
@model JsonSerializer.MyModel
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JsonSerializer</title>
</head>
<body>
<div>
Number is @Json.Serialize(Model.Number)
</div>
<div>
ConvertedNumber is @Json.Serialize(Model.ConvertedNumber)
</div>
</body>
</html>
我的 Controller 、模型和自定义 Json 转换器:
public class HomeController : Controller
{
public IActionResult Index()
{
var model = new MyModel
{
Number = 1.234567,
ConvertedNumber = 1.234567
};
return View("~/Views/Home/Index.cshtml", model);
}
}
public class MyModel
{
public double Number { get; set; }
[JsonConverter(typeof(NumberConverter))]
public double ConvertedNumber { get; set; }
}
public class NumberConverter : JsonConverter<double>
{
public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
{
writer.WriteStringValue($"{value:F2}");
}
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
这呈现:
号码是1.234567
转换后的数字是 1.234567
但是,如果我将 NumberConverter 添加到全局 JSON 选项中,它将起作用,但这会将客户转换器应用到具有 double 类型的每个属性:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews().AddJsonOptions(c=>
{
c.JsonSerializerOptions.Converters.Add(new NumberConverter());
});
}
这呈现:
数字是“1.23”
转换后的数字为“1.23”
这不是有点不一致还是我遗漏了什么?属性和全局设置要么都应该调用自定义转换器,要么都不应该。我检查过,.NET Core 2.2 中使用 Newtonsoft Json 进行序列化是相同的。
您可能会问将单个 double 属性序列化为 JSON 有何意义,但有时该属性可能是更复杂的类型,例如列表或您想要自定义 JSON 输出的其他对象。
最佳答案
原因
Isn't this a bit inconsistent or am I missing something?
实际上,这是 System.Text.JSON
API 的预期行为。如您所知,ASP.NET Core 从 3.0 开始默认使用新的 System.Text.Json。 System.Text.JSON
的新 JsonConverter
与 Newtonsoft.JSON
有很大不同。至于这种情况,官方文档对此进行了介绍。请参阅Converter registration precedence :
During serialization or deserialization, a converter is chosen for each JSON element in the following order, listed from highest priority to lowest:
- [JsonConverter] applied to a property.
- A converter added to the Converters collection.
- [JsonConverter] applied to a custom value type or POCO. If multiple custom converters for a type are registered in the Converters collection, the first converter that returns true for CanConvert is used.
A built-in converter is chosen only if no applicable custom converter is registered.)
您的转换器不会被调用,因为您实际上传递的是 @Model.ConvertedNumber
属性值(这是一个 double 值)而不是 @Model
:
<div>
ConvertedNumber is @Json.Serialize(Model.ConvertedNumber)
</div>
表达式@Model.ConvertedNumber
是计算为 double 值的表达式,而@Model
是计算为MyModel<的表达式
实例。
您的上述调用实际上在幕后调用 Json.Serialize(a_double_value)
。根据上述优先级,整个流程为:
- Razor 发现有一个函数调用
@Json.Serialize(Model.ConvertedNumber)
。由于是函数调用,所以首先必须知道参数。 - 计算参数表达式
Model.ConvertedNumber
,然后获取 double 值 - 将
double
结果传递到Json.Serialize(a_double_value)
- 由于
double
类型没有要序列化的子属性,因此规则 1 根本不适用。 - 由于没有转换器集合,因此规则 2 不适用
- 由于
double
类型没有[JsonConverter]
属性,因此规则 3 不适用 - 最后,选择默认的内置转换器。
如何在System.Text.Json
中应用JsonConverterAttribute
?
根据上面的分析,如果您不想将其添加到全局转换器集合中,则可以传递类似的 Model
@Json.Serialize(Model) // honor the converter because System.Text.Json knows that property has a `[JsonConverterAttribute]`
或手动传递额外选项:
@{
var jsonSerializerOptions =new JsonSerializerOptions();
jsonSerializerOptions.Converters.Add(a_dobule_converter);
}
@JsonSerializer.Serialize(Model.Date, jsonSerializerOptions)
关于c# - Razor @Json.Serialize 对各个属性的处理在 ASP.NET Core 中不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59375381/