如何在C#类中动态处理JSON对象名称的频繁变更以实现反序列化?
解决C# JSON反序列化中键名称变更的非手动方案
1. 利用System.Text.Json的多别名支持(.NET 7+)
从.NET 7开始,JsonPropertyNameAttribute支持指定多个别名,同一个属性可以匹配JSON中的多个键名,无需修改类结构适配不同版本的JSON。
示例代码:
public class EngineData { [JsonPropertyName("EngineTemp")] [JsonPropertyName("EngTemp")] public int EngineTemperature { get; set; } } // 两种键名均能正常反序列化 var json1 = "{\"EngineTemp\":400}"; var data1 = JsonSerializer.Deserialize<EngineData>(json1); var json2 = "{\"EngTemp\":400}"; var data2 = JsonSerializer.Deserialize<EngineData>(json2);
该方案适合键名变更少、可提前预判别名的场景,无需额外编写复杂逻辑。
2. 自定义JsonConverter实现灵活键映射
如果键名变更频繁且无法提前预判,可编写自定义JsonConverter,通过维护键名映射字典,自动将不同JSON键映射到目标类的属性上。
示例代码:
public class FlexibleEngineDataConverter : JsonConverter<EngineData> { // 键名映射表:key为JSON可能的键名,value为类属性名 private readonly Dictionary<string, string> _keyMappings = new() { {"EngineTemp", nameof(EngineData.EngineTemperature)}, {"EngTemp", nameof(EngineData.EngineTemperature)}, // 新增别名只需添加到字典即可 }; public override EngineData Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var data = new EngineData(); using (var doc = JsonDocument.ParseValue(ref reader)) { foreach (var property in doc.RootElement.EnumerateObject()) { if (_keyMappings.TryGetValue(property.Name, out var propName)) { var prop = typeof(EngineData).GetProperty(propName); prop?.SetValue(data, property.Value.GetInt32()); } // 处理未变更的属性 else { var prop = typeof(EngineData).GetProperty(property.Name); if (prop != null) { var value = JsonSerializer.Deserialize(property.Value.GetRawText(), prop.PropertyType); prop.SetValue(data, value); } } } } return data; } public override void Write(Utf8JsonWriter writer, EngineData value, JsonSerializerOptions options) { // 序列化时统一输出标准键名 JsonSerializer.Serialize(writer, new { EngineTemp = value.EngineTemperature }); } } // 使用转换器 var options = new JsonSerializerOptions(); options.Converters.Add(new FlexibleEngineDataConverter()); var json = "{\"EngTemp\":400}"; var data = JsonSerializer.Deserialize<EngineData>(json, options);
该方案无需修改实体类,所有键名映射集中维护,适合大量键名频繁变更的场景。
3. 使用Newtonsoft.Json的自定义ContractResolver(适配Newtonsoft环境)
如果项目使用Newtonsoft.Json,可通过自定义IContractResolver实现多键名匹配。
示例代码:
public class AliasContractResolver : DefaultContractResolver { private readonly Dictionary<string, string> _propertyAliases; public AliasContractResolver(Dictionary<string, string> propertyAliases) { _propertyAliases = propertyAliases; } protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { var property = base.CreateProperty(member, memberSerialization); // 为属性绑定多个别名 if (_propertyAliases.TryGetValue(property.PropertyName, out var alias)) { var originalName = property.PropertyName; property.PropertyName = null; property.ValueProvider = new AliasValueProvider(member, _propertyAliases.Where(kv => kv.Value == originalName).Select(kv => kv.Key).ToList()); } return property; } } public class AliasValueProvider : IValueProvider { private readonly MemberInfo _member; private readonly List<string> _aliases; public AliasValueProvider(MemberInfo member, List<string> aliases) { _member = member; _aliases = aliases; } public void SetValue(object target, object value) { if (_member is PropertyInfo prop) prop.SetValue(target, value); else if (_member is FieldInfo field) field.SetValue(target, value); } public object GetValue(object target) { if (_member is PropertyInfo prop) return prop.GetValue(target); else if (_member is FieldInfo field) return field.GetValue(target); return null; } } // 使用示例 var aliases = new Dictionary<string, string> { {"EngineTemp", "EngineTemperature"}, {"EngTemp", "EngineTemperature"} }; var settings = new JsonSerializerSettings { ContractResolver = new AliasContractResolver(aliases) }; var json = "{\"EngTemp\":400}"; var data = JsonConvert.DeserializeObject<EngineData>(json, settings);
该方案适配已使用Newtonsoft.Json的项目,同样支持集中管理键名映射,避免修改实体类。
内容的提问来源于stack exchange,提问作者Antonio Corneal
相关产品推荐
相关产品推荐

