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

.NET 6中如何从动态JSON生成OpenApiSchema并对接Swashbuckle?

.NET 6 动态生成 OpenApiSchema 实现思路

1. 核心依赖选型

直接使用官方的Microsoft.OpenApi库构建OpenAPI规范对象(Swashbuckle底层依赖此库,天然兼容OpenAPI V3标准),搭配.NET 6原生的System.Text.Json解析JSON负载,避免引入第三方工具,保证轻量和性能。

2. 递归解析JSON生成OpenApiSchema

编写递归方法遍历JSON节点,将JSON类型映射为对应的OpenApiSchema类型,同时处理嵌套对象、数组、格式推断等场景:

  • 基础类型映射:字符串/数字/布尔值直接对应OpenApiSchema.Type,字符串值若符合UUID、日期等格式,自动添加Format属性
  • 对象类型:创建object类型的Schema,递归处理每个属性,收集必填字段(可根据业务规则,比如非null属性标记为必填),并将嵌套对象提取为独立Schema加入components.schemas,用引用关联
  • 数组类型:创建array类型的Schema,递归解析数组元素得到Items属性
  • Null值处理:设置Nullable = true,同时可根据上下文推断基础类型(如默认设为string)

示例核心递归代码片段:

private OpenApiSchema GenerateSchemaFromJsonElement(JsonElement element, Dictionary<string, OpenApiSchema> componentsSchemas, ref string schemaName)
{
    var schema = new OpenApiSchema();
    switch (element.ValueKind)
    {
        case JsonValueKind.String:
            schema.Type = "string";
            if (Guid.TryParse(element.GetString(), out _))
                schema.Format = "uuid";
            // 可扩展日期、邮箱等格式判断逻辑
            break;
        case JsonValueKind.Number:
            schema.Type = element.TryGetInt64(out _) ? "integer" : "number";
            break;
        case JsonValueKind.True:
        case JsonValueKind.False:
            schema.Type = "boolean";
            break;
        case JsonValueKind.Object:
            schema.Type = "object";
            schema.Properties = new Dictionary<string, OpenApiSchema>();
            schema.Required = new HashSet<string>();
            schema.AdditionalProperties = new OpenApiSchema { Type = "boolean", Default = false }; // 禁止额外属性
            foreach (var prop in element.EnumerateObject())
            {
                var propSchema = GenerateSchemaFromJsonElement(prop.Value, componentsSchemas, ref schemaName);
                schema.Properties.Add(prop.Name, propSchema);
                if (prop.Value.ValueKind != JsonValueKind.Null)
                    schema.Required.Add(prop.Name);
                
                // 提取嵌套对象为独立Schema
                if (prop.Value.ValueKind == JsonValueKind.Object)
                {
                    var childSchemaName = PascalCase(prop.Name);
                    if (!componentsSchemas.ContainsKey(childSchemaName))
                        componentsSchemas.Add(childSchemaName, propSchema);
                    // 替换为引用
                    schema.Properties[prop.Name] = new OpenApiSchema
                    {
                        Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = childSchemaName }
                    };
                }
            }
            break;
        case JsonValueKind.Array:
            schema.Type = "array";
            if (element.GetArrayLength() > 0)
                schema.Items = GenerateSchemaFromJsonElement(element.EnumerateArray().First(), componentsSchemas, ref schemaName);
            break;
        case JsonValueKind.Null:
            schema.Nullable = true;
            schema.Type = "string"; // 默认基础类型,可根据业务调整
            break;
    }
    return schema;
}

// 辅助方法:蛇形/小写命名转大驼峰
private string PascalCase(string input)
{
    return string.Concat(input.Split('_', '-').Select(word => char.ToUpper(word[0]) + word.Substring(1)));
}

3. 构建完整OpenAPI规范

生成根Schema后,将其加入components.schemas,再构建Info、Paths、RequestBody等完整结构:

public OpenApiDocument GenerateOpenApiSpec(string jsonPayload, string apiPath, string operationId, string schemaName)
{
    var doc = new OpenApiDocument
    {
        OpenApiVersion = "3.0.1",
        Info = new OpenApiInfo { Title = "Sample API Capabilities", Version = "v1" },
        Paths = new OpenApiPaths(),
        Components = new OpenApiComponents { Schemas = new Dictionary<string, OpenApiSchema>() }
    };

    // 解析JSON负载
    var jsonDoc = JsonDocument.Parse(jsonPayload);
    var rootSchema = GenerateSchemaFromJsonElement(jsonDoc.RootElement, doc.Components.Schemas, ref schemaName);
    doc.Components.Schemas.Add(schemaName, rootSchema);

    // 构建POST接口
    var postOperation = new OpenApiOperation
    {
        OperationId = operationId,
        Tags = new List<OpenApiTag> { new OpenApiTag { Name = "Dynamic Objects" } },
        RequestBody = new OpenApiRequestBody
        {
            Content = new Dictionary<string, OpenApiMediaType>
            {
                ["application/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = schemaName } } },
                ["text/json"] = new OpenApiMediaType { Schema = new OpenApiSchema { Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = schemaName } } },
                ["application/*+json"] = new OpenApiMediaType { Schema = new OpenApiSchema { Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = schemaName } } }
            }
        },
        Responses = new OpenApiResponses { ["200"] = new OpenApiResponse { Description = "Success" } }
    };
    doc.Paths.Add(apiPath, new OpenApiPathItem { Post = postOperation });

    return doc;
}

4. 适配频繁修改的JSON负载

  • 每次收到新JSON时,重新生成完整OpenAPI规范
  • 用JSON内容的哈希值作为缓存键,缓存已生成的Schema和规范,避免重复计算
  • 在递归方法中记录已处理的Schema名称,处理可能的循环引用

5. 集成Swashbuckle(可选)

实现IDocumentFilter,在Swagger文档生成时动态注入动态生成的路径和Schema:

public class DynamicSchemaDocumentFilter : IDocumentFilter
{
    private readonly string _jsonPayload;
    private readonly string _apiPath;
    private readonly string _operationId;
    private readonly string _schemaName;

    public DynamicSchemaDocumentFilter(string jsonPayload, string apiPath, string operationId, string schemaName)
    {
        _jsonPayload = jsonPayload;
        _apiPath = apiPath;
        _operationId = operationId;
        _schemaName = schemaName;
    }

    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        var generator = new DynamicOpenApiGenerator();
        var dynamicDoc = generator.GenerateOpenApiSpec(_jsonPayload, _apiPath, _operationId, _schemaName);
        
        // 合并到现有文档
        foreach (var schema in dynamicDoc.Components.Schemas)
            swaggerDoc.Components.Schemas.TryAdd(schema.Key, schema.Value);
        foreach (var path in dynamicDoc.Paths)
            swaggerDoc.Paths.TryAdd(path.Key, path.Value);
    }
}

注册到SwaggerGen:

builder.Services.AddSwaggerGen(c =>
{
    // 从业务数据源获取动态JSON
    var dynamicJson = "{...}";
    c.DocumentFilter<DynamicSchemaDocumentFilter>(dynamicJson, "/api/DataRecord/UpsertDynamicObj12", "UpsertDynamicExample", "DynamicObj12");
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 00:05:34