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

如何解决自定义OpenApiSchema生成器的递归栈溢出等问题

解决自定义OpenApiSchema生成器的三个核心问题

1. 解决同一类型OpenApiSchema重复生成的问题

核心思路是维护一个类型-Schema/引用的缓存字典,确保每个类型仅生成一次Schema,后续直接复用已有实例或引用:

// 缓存已处理类型对应的Schema,避免重复生成
private readonly Dictionary<Type, OpenApiSchema> _schemaCache = new();

public OpenApiSchema GenerateSchema(Type type)
{
    // 先检查缓存,存在则直接返回
    if (_schemaCache.TryGetValue(type, out var existingSchema))
    {
        return existingSchema;
    }

    // 初始化当前类型的Schema并存入缓存,提前占位
    var schema = new OpenApiSchema();
    _schemaCache[type] = schema;

    // 填充Schema的属性、类型、特性元数据等逻辑
    // ...

    return schema;
}

2. 解决循环引用导致的栈溢出问题

结合上述缓存机制,在递归处理属性时,若遇到已存入缓存(即使未完成生成)的类型,直接返回OpenApiReference而非继续递归,打断循环链路:

public OpenApiSchema GenerateSchema(Type type)
{
    if (_schemaCache.TryGetValue(type, out var existingSchema))
    {
        // 若Schema未完成填充(循环引用场景),返回引用占位
        if (existingSchema.Properties == null || !existingSchema.Properties.Any())
        {
            return new OpenApiSchema
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.Schema,
                    Id = type.Name // 用类型名作为Schema的唯一标识
                }
            };
        }
        return existingSchema;
    }

    var schema = new OpenApiSchema
    {
        Type = "object",
        Properties = new Dictionary<string, OpenApiSchema>()
    };
    _schemaCache[type] = schema;

    // 遍历属性并递归生成Schema
    foreach (var prop in type.GetProperties())
    {
        var propSchema = GenerateSchema(prop.PropertyType);
        schema.Properties.Add(prop.Name, propSchema);
    }

    // 给当前Schema设置引用标识,供其他类型引用
    schema.Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = type.Name };
    return schema;
}

关键逻辑是先缓存占位,再递归处理属性,循环引用时会直接返回引用,避免无限递归导致栈溢出。

3. 优化递归代码的可读性

将大段递归逻辑拆分为多个单一职责的方法,避免嵌套过深,也可以用迭代方式替代递归:

拆分单一职责方法

// 入口方法
public OpenApiSchema GenerateSchema(Type type)
{
    if (_schemaCache.TryGetValue(type, out var existing)) return GetSchemaOrReference(existing);

    var schema = CreateBaseSchema(type);
    _schemaCache[type] = schema;

    PopulateSchemaProperties(schema, type);
    ApplyAttributeMetadata(schema, type); // 处理ApiCommands、ApiArgument等特性

    return schema;
}

// 判断返回完整Schema还是引用占位
private OpenApiSchema GetSchemaOrReference(OpenApiSchema schema)
{
    return schema.Properties?.Any() ?? false 
        ? schema 
        : new OpenApiSchema { Reference = schema.Reference };
}

// 创建基础Schema结构(区分值类型/引用类型)
private OpenApiSchema CreateBaseSchema(Type type)
{
    if (type.IsValueType || type == typeof(string))
    {
        return new OpenApiSchema { Type = MapToOpenApiType(type) };
    }
    return new OpenApiSchema { Type = "object", Properties = new Dictionary<string, OpenApiSchema>() };
}

// 填充类型的属性Schema
private void PopulateSchemaProperties(OpenApiSchema schema, Type type)
{
    foreach (var prop in type.GetProperties())
    {
        var propSchema = GenerateSchema(prop.PropertyType);
        schema.Properties.Add(prop.Name, propSchema);
    }
}

// 处理ApiCommands/ApiArgument等特性的元数据
private void ApplyAttributeMetadata(OpenApiSchema schema, Type type)
{
    var apiCommandAttr = type.GetCustomAttribute<ApiCommandsAttribute>();
    if (apiCommandAttr != null)
    {
        schema.Description = apiCommandAttr.Description;
    }
    // 处理其他特性逻辑
}

用迭代替代递归(可选)

如果递归层级过深,可改用队列/栈遍历类型,进一步降低栈溢出风险并提升可读性:

public OpenApiSchema GenerateSchema(Type rootType)
{
    var processQueue = new Queue<(Type Type, OpenApiSchema Schema)>();
    var rootSchema = CreateBaseSchema(rootType);
    _schemaCache[rootType] = rootSchema;
    processQueue.Enqueue((rootType, rootSchema));

    while (processQueue.Count > 0)
    {
        var (currentType, currentSchema) = processQueue.Dequeue();
        foreach (var prop in currentType.GetProperties())
        {
            var propType = prop.PropertyType;
            if (!_schemaCache.TryGetValue(propType, out var propSchema))
            {
                propSchema = CreateBaseSchema(propType);
                _schemaCache[propType] = propSchema;
                processQueue.Enqueue((propType, propSchema));
            }
            currentSchema.Properties.Add(prop.Name, GetSchemaOrReference(propSchema));
        }
        ApplyAttributeMetadata(currentSchema, currentType);
    }
    return rootSchema;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:25:30