如何在C#中使用Newtonsoft将两个属性序列化为一个JSON属性
解决Json.NET序列化时多属性映射同一JSON键的冲突问题
方法一:通过公共属性封装逻辑(最直观的方案)
修改ScanInfo类,将两个版本的字段改为私有,通过一个公共属性统一对外提供非空的扫描结果,序列化时仅处理该属性:
public class ScanInfo { // 私有字段,仅内部维护 private ScanResult _oldVersion; private ScanResultNew _newVersion; [JsonProperty("scanResult", NullValueHandling = NullValueHandling.Ignore)] public object ScanResult { get { // 优先返回新版本,若为空则返回旧版本(可根据需求调整优先级) return _newVersion ?? _oldVersion; } // 如需支持反序列化,可添加赋值逻辑 set { if (value is ScanResultNew newResult) _newVersion = newResult; else if (value is ScanResult oldResult) _oldVersion = oldResult; } } // 对外提供设置方法,确保两个版本始终仅存一个非空值 public void SetOldScanResult(ScanResult result) { _oldVersion = result; _newVersion = null; } public void SetNewScanResult(ScanResultNew result) { _newVersion = result; _oldVersion = null; } }
这种方案完全规避了属性名冲突,同时通过设置方法保证业务逻辑的一致性,序列化时自然只会输出一个scanResult键。
方法二:自定义ContractResolver(不修改原有类结构)
如果无法改动ScanInfo的原有字段结构,可以通过自定义契约解析器,动态控制序列化行为:
public class ScanInfoContractResolver : DefaultContractResolver { protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { var properties = base.CreateProperties(type, memberSerialization); if (type != typeof(ScanInfo)) return properties; // 找到原有两个字段的配置 var oldVersionProp = properties.First(p => p.UnderlyingName == nameof(ScanInfo.oldVersion)); var newVersionProp = properties.First(p => p.UnderlyingName == nameof(ScanInfo.newVersion)); // 移除原有冲突的配置 properties.Remove(oldVersionProp); properties.Remove(newVersionProp); // 添加自定义的统一属性配置 var combinedProp = new JsonProperty { PropertyName = "scanResult", NullValueHandling = NullValueHandling.Ignore, Readable = true, Writable = false, // 不需要反序列化可设为false ValueProvider = new ScanResultValueProvider() }; properties.Add(combinedProp); return properties; } } public class ScanResultValueProvider : IValueProvider { public object GetValue(object target) { var scanInfo = target as ScanInfo; // 返回非空的扫描结果 return scanInfo.newVersion ?? scanInfo.oldVersion; } public void SetValue(object target, object value) { // 反序列化逻辑按需实现 var scanInfo = target as ScanInfo; if (value is ScanResultNew newResult) scanInfo.newVersion = newResult; else if (value is ScanResult oldResult) scanInfo.oldVersion = oldResult; } }
使用时需要指定自定义解析器:
var serializerSettings = new JsonSerializerSettings { ContractResolver = new ScanInfoContractResolver() }; string json = JsonConvert.SerializeObject(yourScanInfoInstance, serializerSettings);
注意事项
不管采用哪种方案,都要保证业务逻辑中oldVersion和newVersion始终仅有一个非空值,避免出现两个结果同时存在时的优先级问题(当前示例中优先返回newVersion,可根据实际需求调整)。
内容的提问来源于stack exchange,提问作者rutonggaoya
相关产品推荐
相关产品推荐

