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

如何用AutoMapper、Json.NET等库基于变更JSON更新父子对象属性

解决方案:用增量JSON更新C#对象

嘿,这个场景我熟!很多系统做增量更新的时候都会遇到这种只传变更属性的情况,我给你分享两种在C#里最实用的实现方式,按需选就行~

首先先补全你没写完的ChildObject定义(方便后续代码示例):

public class ChildObject
{
    public int ChildID { get; set; }
    public string ChildProp1 { get; set; }
    public string ChildProp2 { get; set; }
}

方法1:手动映射更新(可控性强,适合属性少的场景)

这种方式的核心思路是:把增量JSON反序列化成临时对象,然后逐个判断临时对象的属性是否有值(非null),有值就覆盖原始对象的对应属性。对于集合类的子对象,通过主键(比如ChildID)匹配后再更新。

实现代码

using Newtonsoft.Json;
using System.Linq;

public class ParentObject
{
    public int ID { get; set; }
    public string Prop1 { get; set; }
    public string Prop2 { get; set; }
    public List<ChildObject> Children { get; set; } = new List<ChildObject>();
}

public static void UpdateParentManually(ParentObject original, string updateJson)
{
    // 反序列化增量JSON,配置NullValueHandling.Ignore保证未传的属性不会被设为null
    var updateTemp = JsonConvert.DeserializeObject<ParentObject>(updateJson, new JsonSerializerSettings
    {
        NullValueHandling = NullValueHandling.Ignore
    });

    // 更新父对象的基础属性(主键ID一般不建议更新,根据业务调整)
    if (updateTemp.Prop1 != null)
        original.Prop1 = updateTemp.Prop1;
    if (updateTemp.Prop2 != null)
        original.Prop2 = updateTemp.Prop2;

    // 处理子对象集合:通过ChildID匹配,只更新变更的子项属性
    if (updateTemp.Children != null && updateTemp.Children.Any())
    {
        foreach (var updatedChild in updateTemp.Children)
        {
            var targetChild = original.Children.FirstOrDefault(c => c.ChildID == updatedChild.ChildID);
            if (targetChild != null)
            {
                if (updatedChild.ChildProp1 != null)
                    targetChild.ChildProp1 = updatedChild.ChildProp1;
                if (updatedChild.ChildProp2 != null)
                    targetChild.ChildProp2 = updatedChild.ChildProp2;
            }
            // 如果业务允许新增子项,这里可以加逻辑:if(targetChild == null) original.Children.Add(updatedChild);
        }
    }
}

方法2:借助Newtonsoft.Json的Populate方法(高效简洁,适合属性多的场景)

JsonConvert.PopulateObject可以直接把JSON中的属性值填充到已有对象上,省去手动逐个判断的麻烦。但要注意:默认情况下它会直接替换整个集合,所以如果需要保留原始集合中未变更的子项,得单独处理集合部分。

实现代码

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Linq;

public static void UpdateParentWithPopulate(ParentObject original, string updateJson)
{
    var updateTemp = JsonConvert.DeserializeObject<ParentObject>(updateJson, new JsonSerializerSettings
    {
        NullValueHandling = NullValueHandling.Ignore
    });

    if (updateTemp.Children != null && updateTemp.Children.Any())
    {
        // 先从JSON中移除Children节点,避免Populate直接替换整个集合
        var jUpdate = JObject.Parse(updateJson);
        jUpdate.Remove("Children");
        var filteredJson = jUpdate.ToString();

        // 先填充父对象的基础属性
        JsonConvert.PopulateObject(filteredJson, original, new JsonSerializerSettings
        {
            NullValueHandling = NullValueHandling.Ignore
        });

        // 单独处理子对象的增量更新,对每个匹配的子项用Populate简化操作
        foreach (var updatedChild in updateTemp.Children)
        {
            var targetChild = original.Children.FirstOrDefault(c => c.ChildID == updatedChild.ChildID);
            if (targetChild != null)
            {
                JsonConvert.PopulateObject(JsonConvert.SerializeObject(updatedChild), targetChild, new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore
                });
            }
            // 新增子项逻辑同上,按需添加
        }
    }
    else
    {
        // 没有子对象变更,直接填充整个对象
        JsonConvert.PopulateObject(updateJson, original, new JsonSerializerSettings
        {
            NullValueHandling = NullValueHandling.Ignore
        });
    }
}

使用示例

// 原始对象
var originalParent = new ParentObject
{
    ID = 1,
    Prop1 = "Original Prop1",
    Prop2 = "Original Prop2",
    Children = new List<ChildObject>
    {
        new ChildObject { ChildID = 101, ChildProp1 = "Original Child1 Prop1", ChildProp2 = "Original Child1 Prop2" },
        new ChildObject { ChildID = 102, ChildProp1 = "Original Child2 Prop1", ChildProp2 = "Original Child2 Prop2" }
    }
};

// 增量JSON:只修改了Prop1和ChildID=101的ChildProp2
var updateJson = @"
{
    ""Prop1"": ""Updated Prop1"",
    ""Children"": [
        {
            ""ChildID"": 101,
            ""ChildProp2"": ""Updated Child1 Prop2""
        }
    ]
}";

// 任选一种方法更新
UpdateParentManually(originalParent, updateJson);
// UpdateParentWithPopulate(originalParent, updateJson);

// 验证结果
Console.WriteLine(originalParent.Prop1); // 输出:Updated Prop1
Console.WriteLine(originalParent.Children[0].ChildProp2); // 输出:Updated Child1 Prop2
Console.WriteLine(originalParent.Children[1].ChildProp1); // 输出:Original Child2 Prop1

关键点总结

  1. NullValueHandling配置:一定要设置NullValueHandling.Ignore,避免未传的属性被覆盖为null。
  2. 集合处理逻辑:如果要保留原始集合中的未变更子项,必须通过主键匹配后单独更新,不能直接替换整个集合。
  3. 业务适配:根据实际需求决定是否允许新增/删除子项,在代码中添加对应的逻辑即可。

内容的提问来源于stack exchange,提问作者goroth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:49:32