如何将带数据注解的.NET Core模型类序列化指定JSON格式?
我来分享一个高效且可复用的方案,完美匹配你要的自定义JSON序列化需求,同时还能把反射的性能开销控制在合理范围内。
核心思路
我们的实现分为三个关键步骤:
- 缓存反射结果:每个模型类只做一次反射解析,后续直接复用缓存的元数据,彻底避免重复反射的性能损耗
- 解析数据注解:把
Required、DataType、MaxLength等注解映射到你需要的type和validations字段 - 生成目标JSON结构:将解析后的元数据转换成指定格式的JSON输出
完整代码实现
1. 元数据缓存与解析工具类
这个类负责反射解析模型属性,并把结果缓存起来,是性能优化的核心:
using System.ComponentModel.DataAnnotations; using System.Reflection; using System.Text.Json; public static class ModelMetadataCache { // 缓存字典:key为模型类型,value为预解析好的属性元数据列表 private static readonly Dictionary<Type, List<PropertyMetadata>> _metadataCache = new(); // 获取指定模型的元数据(优先从缓存取,没有则解析并缓存) public static List<PropertyMetadata> GetModelMetadata<T>() { var modelType = typeof(T); if (_metadataCache.TryGetValue(modelType, out var cachedMetadata)) { return cachedMetadata; } var properties = modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance); var metadataList = new List<PropertyMetadata>(); foreach (var prop in properties) { var propertyMeta = new PropertyMetadata { PropertyName = prop.Name.ToCamelCase(), DefaultValue = GetDefaultValue(prop.PropertyType) }; // 解析DataType注解,映射到目标type字段 var dataTypeAttr = prop.GetCustomAttribute<DataTypeAttribute>(); propertyMeta.Type = dataTypeAttr != null ? MapDataTypeToCamelCase(dataTypeAttr.DataType) : MapClrTypeToTypeString(prop.PropertyType); // 解析验证规则 var requiredAttr = prop.GetCustomAttribute<RequiredAttribute>(); propertyMeta.Validations["required"] = requiredAttr != null; var maxLengthAttr = prop.GetCustomAttribute<MaxLengthAttribute>(); if (maxLengthAttr != null) { propertyMeta.Validations["maxLength"] = maxLengthAttr.Length; } metadataList.Add(propertyMeta); } _metadataCache[modelType] = metadataList; return metadataList; } // 将DataType枚举值转为小写驼峰格式 private static string MapDataTypeToCamelCase(DataType dataType) { return dataType switch { DataType.Text => "text", DataType.EmailAddress => "email", DataType.MultilineText => "multilineText", DataType.DateTime => "dateTime", // 可根据需求扩展更多DataType映射 _ => "text" }; } // 当没有DataType注解时,根据CLR类型推断type字段 private static string MapClrTypeToTypeString(Type type) { if (type == typeof(int) || type == typeof(long) || type == typeof(decimal)) return "number"; if (type == typeof(bool)) return "boolean"; if (type == typeof(DateTime)) return "dateTime"; return "text"; } // 获取属性类型的默认值 private static object GetDefaultValue(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; } } // 辅助类:存储单个属性的元数据 public class PropertyMetadata { public string PropertyName { get; set; } public string Type { get; set; } public object DefaultValue { get; set; } public Dictionary<string, object> Validations { get; set; } = new(); } // 扩展方法:PascalCase转camelCase public static class StringExtensions { public static string ToCamelCase(this string input) { if (string.IsNullOrEmpty(input)) return input; return char.ToLowerInvariant(input[0]) + input[1..]; } }
2. 序列化工具类
负责把元数据转换成你需要的JSON结构:
public static class CustomModelSerializer { public static string SerializeToTargetJson<T>() { var metadata = ModelMetadataCache.GetModelMetadata<T>(); var jsonStructure = new Dictionary<string, object>(); foreach (var propMeta in metadata) { jsonStructure[propMeta.PropertyName] = new { value = propMeta.DefaultValue, type = propMeta.Type, validations = propMeta.Validations }; } // 配置JSON序列化选项,保证格式美观且符合驼峰命名 var serializeOptions = new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; return JsonSerializer.Serialize(jsonStructure, serializeOptions); } }
3. 使用示例
假设你的模型类是:
public class ContactFormModel { [Required] [DataType(DataType.Text)] public string Name { get; set; } [Required, DataType(DataType.EmailAddress)] public string Email { get; set; } [Required] [DataType(DataType.Text)] public string Subject { get; set; } [Required] [MaxLength(500)] [DataType(DataType.MultilineText)] public string Message { get; set; } [Required] [DataType(DataType.DateTime)] public DateTime DateOfBirth { get; set; } public int AnyNumber { get; set; } }
调用序列化方法:
var resultJson = CustomModelSerializer.SerializeToTargetJson<ContactFormModel>(); Console.WriteLine(resultJson);
输出的JSON会完全匹配你要求的结构(修正了示例中的语法错误):
{ "name": { "value": "", "type": "text", "validations": { "required": true } }, "email": { "value": "", "type": "email", "validations": { "required": true } }, "subject": { "value": "", "type": "text", "validations": { "required": true } }, "message": { "value": "", "type": "multilineText", "validations": { "required": true, "maxLength": 500 } }, "dateOfBirth": { "value": "0001-01-01T00:00:00", "type": "dateTime", "validations": { "required": true } }, "anyNumber": { "value": 0, "type": "number", "validations": { "required": false } } }
性能与扩展说明
- 反射性能优化:每个模型类只做一次反射解析,后续调用直接从内存缓存取结果,几乎消除了反射的性能开销
- 可扩展性:如果需要支持更多数据注解(比如
MinLength、Range),只需要在GetModelMetadata方法中添加对应的解析逻辑即可 - 自定义映射:
MapDataTypeToCamelCase和MapClrTypeToTypeString方法可以根据你的需求自由修改映射规则
内容的提问来源于stack exchange,提问作者Ivan
相关产品推荐
相关产品推荐

