如何使用JSON.NET反序列化时保留现有只读集合与对象仅更新叶节点属性值
问题核心原因
你自定义的SubListConverter的ReadJson未触发,是因为Json.NET默认判定无setter的只读集合属性不可写入,直接跳过反序列化流程。同时默认的集合反序列化逻辑仅支持替换实例/追加元素两种模式,不支持按索引更新现有集合内对象的属性。
可行解决方案
你可以通过通用固定集合Json转换器+全局序列化配置实现需求,无需修改原有模型加注解:
1. 实现通用转换器
public class FixedSizeReadOnlyCollectionConverter : JsonConverter { public override bool CanConvert(Type objectType) { // 匹配所有元素为类的ReadOnlyCollection<T>类型 if (!objectType.IsGenericType) return false; var genericTypeDef = objectType.GetGenericTypeDefinition(); if (genericTypeDef != typeof(ReadOnlyCollection<>)) return false; return objectType.GenericTypeArguments[0].IsClass; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 你的场景下existingValue一定不为空,为空直接跳过处理 if (existingValue is not System.Collections.IList existingList) { reader.Skip(); return existingValue; } // 读取JSON数组 var jArray = JArray.Load(reader); // 按索引逐个填充现有对象的属性 for (int i = 0; i < Math.Min(jArray.Count, existingList.Count); i++) { var existingElement = existingList[i]; using var elementReader = jArray[i].CreateReader(); serializer.Populate(elementReader, existingElement); } // 直接返回原有集合实例,不新建 return existingValue; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // 走默认序列化逻辑即可 serializer.Serialize(writer, value); } }
2. 调用时传入配置
无需修改原有模型,仅在调用PopulateObject时传入包含该转换器的配置即可:
var model1 = new MainModel(); model1.Subs[1].B = 5; var json = JsonConvert.SerializeObject(model1); var model2 = new MainModel(); // 构造序列化配置 var settings = new JsonSerializerSettings { Converters = new List<JsonConverter> { new FixedSizeReadOnlyCollectionConverter() } }; JsonConvert.PopulateObject(json, model2, settings);
验证结果
执行后model2.Subs[1].B的值会被正确更新为5,完全满足你的要求:
- 全程复用
MainModel、ReadOnlyCollection<SubModel>、所有SubModel的现有实例 - 无新对象、新集合实例创建
- 仅更新
SubModel的A、B叶节点属性 - 无需修改原有模型加任何Json相关注解
扩展说明
如果你的场景还有其他固定大小的集合类型,只需修改CanConvert方法的匹配规则即可覆盖;如果需要校验JSON数组与现有集合长度一致性,可在ReadJson中增加判断抛出异常。
内容的提问来源于stack exchange,提问作者Sjoerd van Kreel
相关产品推荐
相关产品推荐

