You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular调用.NET Web API:多态JSON序列化反序列化及栈溢出问题

解决.NET Web API与Angular间抽象类集合的JSON反序列化问题

咱们先把你的问题拆解清楚:你需要在Angular客户端调用.NET Web API时,正确反序列化包含抽象类Detector(子类TextDetector、ColorDetector)集合的Model对象。最初用TypeNameHandling.Auto的方案因为客户端难以处理带$前缀的类型标记而放弃,后来尝试基于Color属性判断类型的自定义JsonConverter,却因为ReadJson方法里的JObject.Load(reader)触发无限循环导致栈溢出,同时还用到了Entity Framework的GetModel方法。

下面一步步给你解决这些问题:

一、先搞定栈溢出的核心问题

你遇到的栈溢出是因为自定义Converter的递归调用:当你在ReadJson里调用JObject.Load(reader)时,Json.NET会再次尝试用这个自定义Converter来处理Detector类型,形成无限循环。

修复方案:临时禁用自定义Converter读取JObject

在ReadJson方法中,创建一个排除当前Converter的JsonSerializer实例,用它来读取JObject,避免递归。这里给你写个完整的DetectorConverter示例:

public class DetectorConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(Detector).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // 创建临时序列化配置,移除当前Converter避免递归
        var tempSettings = new JsonSerializerSettings();
        tempSettings.Converters.Remove(this);
        var tempSerializer = JsonSerializer.Create(tempSettings);

        // 用临时序列化器读取JObject
        var jObject = JObject.Load(reader, tempSerializer);

        // 根据Color属性判断具体子类
        Detector detector = jObject.ContainsKey("color") 
            ? new ColorDetector() 
            : new TextDetector();

        // 用原序列化器填充对象属性
        serializer.Populate(jObject.CreateReader(), detector);
        return detector;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // 序列化时用默认逻辑即可,无需额外处理
        serializer.Serialize(writer, value);
    }
}

二、配置.NET Web API使用自定义Converter

接下来要把这个Converter配置到Web API的序列化设置里,同时替换掉原来的TypeNameHandling.Auto:

如果是.NET 6+(Program.cs)

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        // 移除TypeNameHandling.Auto,使用默认配置即可
        options.JsonSerializerOptions.Converters.Add(new DetectorConverter());
    });

如果是旧版本(Startup.cs)

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddNewtonsoftJson(options =>
        {
            options.SerializerSettings.TypeNameHandling = TypeNameHandling.Object;
            options.SerializerSettings.Converters.Add(new DetectorConverter());
        });
}

也可以直接在抽象类Detector上标记特性,配置更直观:

[JsonConverter(typeof(DetectorConverter))]
public abstract class Detector
{
    // 公共属性,比如Bounds
    public Bounds Bounds { get; set; }
}

三、Entity Framework GetModel方法的注意事项

你的GetModel方法要确保正确加载Detectors集合,避免EF延迟加载导致序列化问题。比如:

public async Task<Model> GetModel(int id)
{
    return await _context.Models
        .Include(m => m.Detectors) // 显式加载Detectors集合,避免延迟加载引发的序列化异常
        .FirstOrDefaultAsync(m => m.Id == id);
}

如果Bounds是复杂类型,也要确保它能被正常序列化,避免和自定义Converter产生冲突。

四、Angular客户端的处理

现在API返回的JSON不会带$type标记了,客户端可以直接根据color属性判断Detector类型。你可以这样定义TypeScript接口:

// 公共Detector接口
interface Detector {
  bounds: { x: number; y: number; width: number; height: number };
  // 其他公共属性
}

// TextDetector子类
interface TextDetector extends Detector {
  text: string;
  // TextDetector特有属性
}

// ColorDetector子类
interface ColorDetector extends Detector {
  color: string;
  // ColorDetector特有属性
}

// 反序列化时转换类型
function parseDetectors(detectors: any[]): (TextDetector | ColorDetector)[] {
  return detectors.map(det => {
    return det.color ? (det as ColorDetector) : (det as TextDetector);
  });
}

// 调用API时使用
this.http.get<{ detectors: any[] }>('/api/models/1')
  .subscribe(response => {
    const parsedDetectors = parseDetectors(response.detectors);
    // 后续处理逻辑
  });

这样客户端就能正确识别不同类型的Detector了。


内容的提问来源于stack exchange,提问作者Sebastien Servouze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:16:01