You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core System.Text.Json枚举增改JSON属性及数组处理泛型方案问题

基于System.Text.Json的对象填充通用实现(支持数组合并/替换)

核心逻辑说明

  • 保留原对象属性值,仅覆盖JSON中存在的字段
  • 数组字段支持两种模式:
    1. 替换模式:replaceArrays=true时直接用传入JSON的数组覆盖原有数组
    2. 合并模式: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 06:36:03