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

如何实现共享JSON Schema定义生成独立C#类供其他类复用?

解决Newtonsoft.Json Schema转C#类时共享定义重复生成的问题

核心问题在于Newtonsoft.Json的JsonSchemaGenerator默认不会追踪外部$ref指向的共享定义,导致业务Schema重复生成共享类型,而单独处理commons.schema.json时因为只有嵌套定义没有根结构生成空类。以下是具体解决步骤:

1. 实现自定义SchemaResolver缓存共享类型

创建一个继承自JsonSchemaResolver的自定义解析器,用来缓存commons中生成的共享类型,后续处理业务Schema时直接复用:

using Newtonsoft.Json.Schema;
using Newtonsoft.Json.Schema.Generation;
using System.Collections.Generic;
using System.Linq;

public class SharedSchemaResolver : JsonSchemaResolver
{
    private readonly Dictionary<string, Type> _sharedTypeCache = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);

    public override Type ResolveSchemaType(JsonSchema schema, JsonSchemaGenerator generator, Type currentType)
    {
        // 识别commons.schema.json中的共享定义
        if (!string.IsNullOrEmpty(schema.Id) && schema.Id.Contains("commons.schema.json#"))
        {
            var typeName = ExtractTypeNameFromRef(schema.Id);
            if (_sharedTypeCache.TryGetValue(typeName, out var cachedType))
            {
                return cachedType;
            }
        }

        var generatedType = base.ResolveSchemaType(schema, generator, currentType);

        // 缓存commons生成的类型
        if (!string.IsNullOrEmpty(schema.Id) && schema.Id.Contains("commons.schema.json#"))
        {
            var typeName = ExtractTypeNameFromRef(schema.Id);
            _sharedTypeCache[typeName] = generatedType;
        }

        return generatedType;
    }

    private string ExtractTypeNameFromRef(string schemaId)
    {
        // 从$ref片段中提取类型名,比如从"commons.schema.json#/definitions/ComponentId"提取"ComponentId"
        var fragment = schemaId.Split('#')[1];
        return fragment.Split('/').Last();
    }
}

2. 预生成commons中的共享类

单独处理commons.schema.json,遍历其内部的Definitions生成对应的C#类,避免空类问题:

using Newtonsoft.Json.Schema;
using Newtonsoft.Json.Schema.Generation;
using System.IO;

// 加载commons.schema.json
var commonsSchema = JsonSchema.Parse(File.ReadAllText("commons.schema.json"));
var schemaGenerator = new JsonSchemaGenerator();
var sharedResolver = new SharedSchemaResolver();
schemaGenerator.Resolver = sharedResolver;

// 遍历所有共享定义并生成类
foreach (var (name, schema) in commonsSchema.Definitions)
{
    schemaGenerator.Generate(schema, typeof(object));
}

3. 处理业务Schema时复用共享类型

使用同一个SharedSchemaResolver实例处理所有业务Schema,确保遇到$ref时直接引用已生成的共享类:

using System.CodeDom.Compiler;
using System.IO;

// 设置代码生成配置
var codeProvider = new CSharpCodeProvider();
var codeGenOptions = new CodeGeneratorOptions
{
    BracingStyle = "C",
    BlankLinesBetweenMembers = true
};

// 遍历所有业务Schema文件
foreach (var schemaFilePath in Directory.GetFiles("business-schemas", "*.schema.json"))
{
    var businessSchema = JsonSchema.Parse(File.ReadAllText(schemaFilePath));
    // 生成业务类(自动复用共享类型)
    var businessType = schemaGenerator.Generate(businessSchema, typeof(object));
    
    // 将生成的类保存为.cs文件
    var outputFilePath = Path.Combine("generated-classes", $"{Path.GetFileNameWithoutExtension(schemaFilePath)}.cs");
    using var writer = new StreamWriter(outputFilePath);
    codeProvider.GenerateCodeFromType(new System.CodeDom.CodeTypeDeclaration(businessType.Name), writer, codeGenOptions);
}

4. 可选:配置命名空间区分共享与业务类

如果需要将共享类和业务类放到不同命名空间,可在生成时切换CSharpGenerationProvider:

// 生成共享类时设置命名空间
schemaGenerator.GenerationProvider = new CSharpGenerationProvider
{
    Namespace = "YourApp.Shared"
};
// 预生成共享类...

// 生成业务类时切换命名空间
schemaGenerator.GenerationProvider = new CSharpGenerationProvider
{
    Namespace = "YourApp.Business"
};
// 处理业务Schema...

注意事项

  • 确保业务Schema中的$ref路径正确指向commons.schema.json,比如./commons.schema.json#/definitions/ComponentId
  • 如果commons.schema.json没有根节点定义,直接遍历Definitions是正确的处理方式,这也是之前生成空类的原因

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 21:08:27