.NET Core System.Text.Json枚举增改JSON属性及数组处理泛型方案问题
基于System.Text.Json的对象填充通用实现(支持数组合并/替换)
核心逻辑说明
- 保留原对象属性值,仅覆盖JSON中存在的字段
- 数组字段支持两种模式:
- 替换模式:
replaceArrays=true时直接用传入JSON的数组覆盖原有数组 - 合并模式:
replaceArrays=false时按元素Id属性匹配,更新已有元素属性,新增不存在的元素
- 替换模式:
- 支持嵌套对象、嵌套数组的递归处理
完整实现代码
using System.Reflection; using System.Text.Json; using System.Collections.Generic; using System.Linq; public class JsonPopulator { // 泛型重载,方便调用 public Task PopulateAsync<T>(T target, string jsonSource, bool replaceArrays = false) { return PopulateObjectAsync(target, jsonSource, typeof(T), replaceArrays); } public async Task PopulateObjectAsync(object target, string source, Type type, bool replaceArrays = false) { using var jsonDoc = JsonDocument.Parse(source); var rootElement = jsonDoc.RootElement; // 处理根节点为数组的场景 if (rootElement.ValueKind == JsonValueKind.Array) { await ProcessArrayAsync(target, rootElement, type, replaceArrays); return; } // 处理根节点为对象的场景 foreach (var property in rootElement.EnumerateObject()) { await OverwritePropertyAsync(target, property, type, replaceArrays); } } private async Task OverwritePropertyAsync(object target, JsonProperty updatedProperty, Type parentType, bool replaceArrays) { var propertyInfo = parentType.GetProperty(updatedProperty.Name, BindingFlags.Public | BindingFlags.Instance); if (propertyInfo == null || !propertyInfo.CanWrite) return; var propertyType = propertyInfo.PropertyType; var propertyValue = updatedProperty.Value; object parsedValue; // 情况1:值类型、字符串、或者开启替换的数组,直接反序列化 if (propertyType.IsValueType || propertyType == typeof(string) || (replaceArrays && propertyValue.ValueKind == JsonValueKind.Array)) { parsedValue = JsonSerializer.Deserialize(propertyValue.GetRawText(), propertyType); } // 情况2:合并模式下的数组,走数组合并逻辑 else if (propertyValue.ValueKind == JsonValueKind.Array) { parsedValue = propertyInfo.GetValue(target); // 如果原有数组为空,先初始化 parsedValue ??= Activator.CreateInstance(propertyType); await ProcessArrayAsync(parsedValue, propertyValue, propertyType, replaceArrays); } // 情况3:复杂对象,递归填充 else { parsedValue = propertyInfo.GetValue(target); // 如果原有属性为空,先初始化实例 parsedValue ??= Activator.CreateInstance(propertyType); await PopulateObjectAsync(parsedValue, propertyValue.GetRawText(), propertyType, replaceArrays); } propertyInfo.SetValue(target, parsedValue); } private async Task ProcessArrayAsync(object targetArray, JsonElement jsonArray, Type arrayType, bool replaceArrays) { // 只处理实现了IList<T>的集合类型,可按需扩展其他集合类型 var genericInterface = arrayType.GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>)); if (genericInterface == null) return; var elementType = genericInterface.GetGenericArguments()[0]; var list = (System.Collections.IList)targetArray; // 获取元素的Id属性作为匹配键,可自定义修改为其他匹配规则 var idProperty = elementType.GetProperty("Id", BindingFlags.Public | BindingFlags.Instance); foreach (var jsonElement in jsonArray.EnumerateArray()) { if (idProperty != null && jsonElement.TryGetProperty("Id", out var jsonId)) { var idValue = JsonSerializer.Deserialize(jsonId.GetRawText(), idProperty.PropertyType); // 查找现有匹配项 var existingItem = list.Cast<object>().FirstOrDefault(item => idValue.Equals(idProperty.GetValue(item))); if (existingItem != null) { // 匹配到现有项,递归填充属性 await PopulateObjectAsync(existingItem, jsonElement.GetRawText(), elementType, replaceArrays); continue; } } // 没有匹配到现有项,反序列化后加入集合 var newItem = JsonSerializer.Deserialize(jsonElement.GetRawText(), elementType); list.Add(newItem); } } }
使用示例
// 初始化原有对象 var initialJson = @"{ ""Title"": ""Startpage"", ""Links"": [ { ""Id"": 10, ""Text"": ""Start"", ""Link"": ""/index"" }, { ""Id"": 11, ""Text"": ""Info"", ""Link"": ""/info"" } ] }"; var pages = JsonSerializer.Deserialize<Pages>(initialJson); // 待更新的JSON var updateJson = @"{ ""Head"": ""Latest news"", ""Links"": [ { ""Id"": 11, ""Text"": ""News"", ""Link"": ""/news"" }, { ""Id"": 21, ""Text"": ""More News"", ""Link"": ""/morenews"" } ] }"; var populator = new JsonPopulator(); // 结果1:数组合并模式 await populator.PopulateAsync(pages, updateJson, replaceArrays: false); // 结果2:数组替换模式,重置初始对象后调用 // pages = JsonSerializer.Deserialize<Pages>(initialJson); // await populator.PopulateAsync(pages, updateJson, replaceArrays: true); // 实体类定义 public class Pages { public string Title { get; set; } public string Head { get; set; } public List<Links> Links { get; set; } } public class Links { public int Id { get; set; } public string Text { get; set; } public string Link { get; set; } }
扩展说明
如果需要自定义数组元素的匹配规则,可以修改ProcessArrayAsync方法中获取匹配键的逻辑,比如传入自定义的主键选择器参数,适配不同的业务场景。如果不需要异步操作,可以去掉所有async/await关键字,方法改为同步实现即可。
内容的提问来源于stack exchange,提问作者Asons
相关产品推荐
相关产品推荐

