ASP.net Core MVC 没有在抽象结果上调用 System.Text.Json 的自定义多态 JsonConverter

ASP.net Core MVC not calling System.Text.Json's custom polymorphic JsonConverter on abstract result

我有一个派生自 Microsoft.AspNetCore.Mvc.ControllerBase 的 .NET Core 3.1 控制器,自定义 json 转换器未在结果输出上调用。

IProduct

public interface IProduct { ... }

控制器:

[HttpGet("{id}")]
public IProduct Get(string id)
{
   IProduct product = _data.GetProduct(id);
   return product;
}

[HttpPut]
public Task Save(IProduct product)
{
    return _data.Save(product);
}

JsonConverter:

public class ProductConverter : System.Text.Json.Serialization.JsonConverter<IProduct> 
{
    public override IProduct Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
         // this code works from unit tests, trust me :)
    }

    public override void Write(Utf8JsonWriter writer, IProduct value, JsonSerializerOptions options) 
    {
        // this code works from unit tests, trust me :)
    }
}

启动:

services
  .AddControllers()
  .AddJsonOptions(options => {
      options.JsonSerializerOptions.Converters.Add(new ProductConverter());
  });

症状

我是漏掉了什么还是做错了什么?

这个 answer by Damien_The_Unbeliever to Why when return interface in a web api method, I get the values of inherited class with interface values? 很旧但显然仍然适用于 ASP.NET Core 3.1:

By the time it hits the Json serializer, it doesn't care what the return type of the method was. All it knows is "I have this object, let's see what I can do with it".

因此,您的转换器未被调用,因为返回的实际具体类型不是 IProduct,因此基础 class 实现 JsonConverter<IProduct>.CanConvert(objectType) returns false:

public override bool CanConvert(Type typeToConvert)
{
    return typeToConvert == typeof(T);
}

要使您的 ProductConverter 也适用于 IProduct 的具体实现,请按如下方式覆盖 CanConvert

public override bool CanConvert(Type typeToConvert) => 
    typeof(IProduct).IsAssignableFrom(typeToConvert);

请注意,如果 Read()Write() 的实现尝试通过使用具体类型递归调用序列化器来生成传入具体产品的“默认”序列化,您可能会得到堆栈溢出。要解决这个问题,您可以:

  • 克隆选项并从转换器列表中删除 ProductConverter,如 .

    所示
  • 传入默认选项而不是传入选项,如 by Demetrius Axenowski to .

  • 将您的 IProduct 包装在某个容器中,并将转换器应用到 属性,例如

    public class ProductContainer 
    { 
        [JsonConverter(typeof(ProductConverter))] 
        public IProduct Product { get; set; } 
    }