如何用JsonSchema.Net.Generation基于带Category特性的类生成自定义JSON Schema
基于Category特性自定义JSON Schema生成方案
需求说明
在.NET 5.0环境下使用JsonSchema.Net.Generation组件,基于带有Category特性的类生成JSON Schema:
- 标注
Category("Student")的属性归到Student节点下 - 标注
Category("Staff")的属性归到Staff节点下 - 标注
Category("Common")的属性保留在根节点
示例类代码
using System.ComponentModel; using Json.Schema.Generation; [Title("Custom JSON Schema generation.")] class School { [DisplayName("Student")] [Category("Student")] [Description("School Schema generation test")] [Const(true)] public string StudentName { get; set; } = "XYZ"; [Editable(true)] [Range(0, 1000, ErrorMessage = "Value for {0} must be between {1} and {2}.")] [Category("Student")] public int Id { get; set; } = 10; [Editable(false)] [DisplayName("ParentName")] [Display(Name = "PPhone", Description = "Testing", GroupName = "Student")] [Category("Student")] public string ParentName { get; set; } [DisplayName("ParentPhone")] [Category("Student")] public string ParentPhone { get; set; } [Editable(true)] [Category("Staff")] public string TeacherName { get; set; } [Editable(true)] [Range(0, 10000, ErrorMessage = "Value for {0} must be between {1} and {2}.")] [DefaultValue(5000)] [Category("Staff")] public int Salary { get; set; } [ReadOnly(true)] [Category("Student")] public bool AvailingTransport { get; set; } = true; [ReadOnly(true)] [Category("Common")] public string Address { get; set; } [ReadOnly(true)] [Category("Common")] public string MobileNumber { get; set; } }
默认生成的扁平化Schema
{ "type": "object", "properties": { "StudentName": { "type": "string", "description": "School Schema generation test", "const": true }, "Id": { "type": "integer" }, "ParentName": { "type": "string" }, "ParentPhone": { "type": "string" }, "TeacherName": { "type": "string" }, "Salary": { "type": "integer" }, "AvailingTransport": { "type": "boolean", "readOnly": true }, "Address": { "$ref": "#/$defs/string" }, "MobileNumber": { "$ref": "#/$defs/string" } }, "title": "Custom JSON Schema generation.", "$defs": { "string": { "type": "string", "readOnly": true } }, "additionalProperties": false }
期望的分组式Schema
{ "type": "object", "properties": { "Student": { "type": "object", "properties": { "StudentName": { "type": "string", "description": "School Schema generation test", "const": true }, "Id": { "type": "integer" }, "ParentName": { "type": "string" }, "ParentPhone": { "type": "string" }, "AvailingTransport": { "type": "boolean", "readOnly": true } }, "additionalProperties": false }, "Staff": { "type": "object", "properties": { "TeacherName": { "type": "string" }, "Salary": { "type": "integer" } }, "additionalProperties": false }, "Address": { "$ref": "#/$defs/string" }, "MobileNumber": { "$ref": "#/$defs/string" } }, "title": "Custom JSON Schema generation.", "$defs": { "string": { "type": "string", "readOnly": true } }, "additionalProperties": false }
解决方案代码
核心思路
先通过默认方式生成扁平化Schema,再结合反射获取属性的Category特性,重新组织Schema的properties结构,将属性归类到对应节点。
完整实现代码
using System; using System.Reflection; using System.Collections.Generic; using System.Linq; using Json.Schema; using Json.Schema.Generation; using System.Text.Json; using System.ComponentModel; class Program { static void Main(string[] args) { // 1. 生成默认扁平化Schema var originalSchema = new JsonSchemaBuilder() .FromType<School>() .AdditionalProperties(false) .Build(); // 2. 通过反射获取每个属性的Category var propertyCategoryMap = typeof(School) .GetProperties(BindingFlags.Public | BindingFlags.Instance) .ToDictionary( prop => prop.Name, prop => prop.GetCustomAttribute<CategoryAttribute>()?.Category ?? "Common" ); // 3. 重新组织properties结构 var rootProperties = new Dictionary<string, JsonSchema>(); var studentProps = new Dictionary<string, JsonSchema>(); var staffProps = new Dictionary<string, JsonSchema>(); foreach (var (propName, propSchema) in originalSchema.Properties) { if (propertyCategoryMap.TryGetValue(propName, out var category)) { switch (category) { case "Student": studentProps.Add(propName, propSchema); break; case "Staff": staffProps.Add(propName, propSchema); break; default: rootProperties.Add(propName, propSchema); break; } } else { rootProperties.Add(propName, propSchema); } } // 4. 添加Student和Staff子节点 if (studentProps.Any()) { rootProperties.Add("Student", new JsonSchema { Type = JsonSchemaType.Object, Properties = studentProps, AdditionalProperties = false }); } if (staffProps.Any()) { rootProperties.Add("Staff", new JsonSchema { Type = JsonSchemaType.Object, Properties = staffProps, AdditionalProperties = false }); } // 5. 创建自定义Schema var customSchema = new JsonSchema { Type = originalSchema.Type, Properties = rootProperties, Title = originalSchema.Title, Defs = originalSchema.Defs, AdditionalProperties = originalSchema.AdditionalProperties }; // 6. 序列化输出 var serializeOptions = new JsonSerializerOptions { WriteIndented = true }; Console.WriteLine(JsonSerializer.Serialize(customSchema, serializeOptions)); } }
代码说明
- 反射获取Category:通过
Type.GetProperties遍历类属性,提取每个属性的CategoryAttribute值,建立属性名与分类的映射 - 属性分组:遍历原Schema的properties,将属性分别放入Student、Staff或根节点的集合中
- 构建子Schema:为Student和Staff分类创建独立的
JsonSchema对象,设置为object类型并包含对应属性 - 组装最终Schema:将分组后的属性集合替换原Schema的properties,保留原Schema的其他元数据(如title、$defs等)
内容的提问来源于stack exchange,提问作者Developer
相关产品推荐
相关产品推荐

