c# - Razor @Json.Serialize 对各个属性的处理在 ASP.NET Core 中不一致

标签 c# json asp.net-core asp.net-core-mvc

简单地说,我有一个简单的 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 的新 JsonConverterNewtonsoft.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:

  1. [JsonConverter] applied to a property.
  2. A converter added to the Converters collection.
  3. [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) 。根据上述优先级,整个流程为:

  1. Razor 发现有一个函数调用 @Json.Serialize(Model.ConvertedNumber)。由于是函数调用,所以首先必须知道参数。
  2. 计算参数表达式Model.ConvertedNumber,然后获取 double 值
  3. double 结果传递到 Json.Serialize(a_double_value)
  4. 由于 double 类型没有要序列化的子属性,因此规则 1 根本不适用。
  5. 由于没有转换器集合,因此规则 2 不适用
  6. 由于 double 类型没有 [JsonConverter] 属性,因此规则 3 不适用
  7. 最后,选择默认的内置转换器。

如何在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/

相关文章:

azure - 您无权查看此目录或页面。 azure

c# - 使用 Contains 在 SQLite 中搜索非英文文本会返回比预期更多的结果

c# - 如何从字节数组制作 Sprite ?

javascript - 如何在同一页面上创建多个谷歌地图(不是标记)?

json - 一个字段的Golang多个json标签名称

c# - RouteExistingFiles 不再是 asp.net 5 的一部分了吗?

c# - 如何在 SSLStream 的 AuthenticateAsServer() 中提供证书链?

c# - Autofac.Web : Impact of having *both* PropertyInjection and AttributedInjection configured

json - rails API : Cannot whitelist JSON field attribute

asp.net-core - Identity Server 4 和 ASP.NET Core 身份