如何实现共享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
相关产品推荐
相关产品推荐

