如何动态生成JsonPropertyName?基于System.Text.Json的多字段映射需求
问题描述
我有如下Model类:
using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; public class FormField { [Required] [JsonPropertyName("STD_USERTYPEID")] public string UserTypeId { get; set; } [Required] [JsonPropertyName("STD_OFFICETYPEID")] public string OfficeTypeId { get; set; } }
存在多种场景:STD_OFFICETYPEID可能以LegacyOFFICETYPEID或OfficeID的形式传入。我正在使用System.Text.Json NuGet包,能否动态生成JsonPropertyName?
注:示例已简化,生产环境中会有20-25个具体属性,每个属性可能对应5-10个不同的JsonPropertyName。
解决方案
System.Text.Json原生的JsonPropertyNameAttribute只支持单个别名,没法直接动态生成多个名称。针对你这种多属性多别名的场景,推荐两种高效实现方式:
1. 自定义JsonConverter(兼容性强)
给FormField写一个自定义转换器,反序列化时遍历属性的所有可能别名,匹配到就赋值。这种方式适配所有.NET版本,性能可控,适合属性和别名较多的场景。
示例代码:
using System.ComponentModel.DataAnnotations; using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; public class FormFieldConverter : JsonConverter<FormField> { // 维护属性与所有别名的映射,生产环境可抽成配置文件 private static readonly Dictionary<string, List<string>> _propertyAliasMap = new() { { nameof(FormField.UserTypeId), new List<string> { "STD_USERTYPEID" } }, { nameof(FormField.OfficeTypeId), new List<string> { "STD_OFFICETYPEID", "LegacyOFFICETYPEID", "OfficeID" } } // 补充其他20+属性的别名映射 }; public override FormField Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var formField = new FormField(); using var jsonDoc = JsonDocument.ParseValue(ref reader); var rootElement = jsonDoc.RootElement; foreach (var (propName, aliases) in _propertyAliasMap) { var property = typeof(FormField).GetProperty(propName); if (property == null) continue; foreach (var alias in aliases) { if (rootElement.TryGetProperty(alias, out var valueElement)) { property.SetValue(formField, valueElement.GetString()); break; // 匹配到第一个有效别名就停止 } } } // 执行数据验证,保留Required特性的作用 var validationContext = new ValidationContext(formField); Validator.ValidateObject(formField, validationContext, validateAllProperties: true); return formField; } public override void Write(Utf8JsonWriter writer, FormField value, JsonSerializerOptions options) { // 序列化时使用默认的JsonPropertyName,也可自定义输出字段名 JsonSerializer.Serialize(writer, value, options); } }
使用时注册转换器即可:
var options = new JsonSerializerOptions(); options.Converters.Add(new FormFieldConverter()); // 测试不同别名的JSON var json1 = "{\"STD_OFFICETYPEID\": \"1\", \"STD_USERTYPEID\": \"admin\"}"; var json2 = "{\"LegacyOFFICETYPEID\": \"2\", \"STD_USERTYPEID\": \"user\"}"; var json3 = "{\"OfficeID\": \"3\", \"STD_USERTYPEID\": \"guest\"}"; var field1 = JsonSerializer.Deserialize<FormField>(json1, options); var field2 = JsonSerializer.Deserialize<FormField>(json2, options); var field3 = JsonSerializer.Deserialize<FormField>(json3, options);
2. 修改JsonTypeInfo元数据(.NET 6+)
如果用的是.NET 6及以上版本,直接通过修改JsonTypeInfo添加多别名,无需写完整转换器,代码更简洁。
示例代码:
using System.ComponentModel.DataAnnotations; using System.Text.Json; using System.Text.Json.Serialization.Metadata; // 定义属性别名映射 var propertyAliases = new Dictionary<string, List<string>> { { nameof(FormField.OfficeTypeId), new List<string> { "LegacyOFFICETYPEID", "OfficeID" } } // 补充其他属性的别名 }; var options = new JsonSerializerOptions(); options.TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { typeInfo => { if (typeInfo.Type != typeof(FormField)) return; foreach (var property in typeInfo.Properties) { if (propertyAliases.TryGetValue(property.Name, out var aliases)) { // 为属性添加额外的匹配别名 foreach (var alias in aliases) { property.PropertyNames.Add(alias); } } } } } }; // 测试反序列化 var json = "{\"OfficeID\": \"4\", \"STD_USERTYPEID\": \"operator\"}"; var formField = JsonSerializer.Deserialize<FormField>(json, options); // 执行验证 var validationContext = new ValidationContext(formField); Validator.ValidateObject(formField, validationContext, validateAllProperties: true);
注意事项
- 两种方式都能保留
Required特性的验证逻辑,记得在反序列化后执行验证步骤。 - 生产环境建议把别名映射抽成外部配置文件(如JSON、XML),避免硬编码,方便后续维护和修改。
内容的提问来源于stack exchange,提问作者tRuEsAtM
相关产品推荐
相关产品推荐

