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

递归遍历多层嵌套对象并修改祖辈对象的C#实现问题

嵌套对象跨层级属性填充的无副作用递归实现

需求说明

在.NET 6的C#控制台应用中,需要递归遍历任意深度的嵌套Obj对象:

  • 遍历到任意层级的对象时,回溯所有已遍历的祖辈对象
  • 根据输入JSON定义的规则修改祖辈对象的指定属性:
    • targetChildren:指定需要获取值的子/孙辈对象名称
    • valsFromChildren:填充为对应目标子对象的valToGive值
    • runtimeValsFromChildren:填充为对应目标子对象生成的运行时随机数

问题现状

现有递归代码存在以下问题:

  • 仅能处理直接父子对象的关联,无法支持跨层级的祖辈-孙辈关联(例如foo的valsFromChildren未包含孙辈quux的giftFromQuux值)
  • 直接修改原对象,存在副作用
  • 递归逻辑错误,仅处理第一个子对象,后续子对象被忽略
  • RuntimeVal每次调用都新建Random实例,容易生成重复随机数

额外需求

实现无副作用的递归逻辑:

  • 不修改原对象,返回独立内存地址的对象副本
  • 支持跨层级回溯所有祖辈对象并更新属性

示例数据与代码

输入JSON

{
    "name": "foo",
    "targetChildren": [
        "baz",
        "quux"
    ],
    "valsFromChildren": [],
    "runtimeValsFromChildren": [],
    "childObjects": [
        {
            "name": "baz",
            "valToGive": "giftFromBaz",
            "targetChildren": [
                "qux"
            ],
            "valsFromChildren": [],
            "runtimeValsFromChildren": [],
            "childObjects": [
                {
                    "name": "qux",
                    "valToGive": "giftFromQux"
                },
                {
                    "name": "quux",
                    "valToGive": "giftFromQuux"
                }
            ]
        }
    ]
}

现有C#代码

using System.Text.Json;
using System.Text.Json.Serialization;
using System.Collections;

var json = "{\"name\":\"foo\",\"targetChildren\":[\"baz\",\"quux\"],\"valsFromChildren\":[],\"runtimeValsFromChildren\":[],\"childObjects\":[{\"name\":\"baz\",\"valToGive\":\"giftFromBaz\",\"targetChildren\":[\"qux\"],\"valsFromChildren\":[],\"runtimeValsFromChildren\":[],\"childObjects\":[{\"name\":\"qux\",\"valToGive\":\"giftFromQux\"},{\"name\":\"quux\",\"valToGive\":\"giftFromQuux\"}]}]}";
var obj = JsonSerializer.Deserialize<Obj>(json);
DoRecursion(obj);
var newObjJson = JsonSerializer.Serialize(obj);

static Obj DoRecursion(Obj obj)
{
    if (obj.ChildObjects == null || obj.ChildObjects.Count <= 0)
        return obj;

    foreach (var child in obj.ChildObjects)
    {
        var parent = obj;
        if (parent.TargetChildren != null && parent.TargetChildren.Contains(child.Name))
        {
            // Give the values to the parent that is targeting them.
            parent.ValsFromChildren.Add(child.ValToGive);
            parent.RuntimeValsFromChildren.Add(child.RuntimeVal);
        }
        return DoRecursion(child);
    }
    return obj;
}

class Obj
{
    [JsonPropertyName("name")]
    public string Name { get; set; }

    [JsonPropertyName("valToGive")]
    public string ValToGive { get; set; }

    [JsonPropertyName("targetChildren")]
    public List<string> TargetChildren { get; set; }

    [JsonPropertyName("valsFromChildren")]
    public List<string> ValsFromChildren { get; set; }

    [JsonPropertyName("runtimeValsFromChildren")]
    public List<int> RuntimeValsFromChildren { get; set; }

    [JsonPropertyName("childObjects")]
    public List<Obj> ChildObjects { get; set; }

    [JsonIgnore]
    public int RuntimeVal => new Random().Next(0, 100);
}

期望输出JSON

{
    "name": "foo",
    "targetChildren": [
        "baz",
        "quux"
    ],
    "valsFromChildren": [
        "giftFromBaz",
        "giftFromQuux"
    ],
    "runtimeValsFromChildren": [
        31,
        88
    ],
    "childObjects": [
        {
            "name": "baz",
            "valToGive": "giftFromBaz",
            "targetChildren": [
                "qux"
            ],
            "valsFromChildren": [
                "giftFromQux"
            ],
            "runtimeValsFromChildren": [
                43
            ],
            "childObjects": [
                {
                    "name": "qux",
                    "valToGive": "giftFromQux"
                },
                {
                    "name": "quux",
                    "valToGive": "giftFromQuux"
                }
            ]
        }
    ]
}

实际输出JSON

{
    "name": "foo",
    "targetChildren": [
        "baz",
        "quux"
    ],
    "valsFromChildren": [
        "giftFromBaz"
    ],
    "runtimeValsFromChildren": [
        43
    ],
    "childObjects": [
        {
            "name": "baz",
            "valToGive": "giftFromBaz",
            "targetChildren": [
                "qux"
            ],
            "valsFromChildren": [
                "giftFromQux"
            ],
            "runtimeValsFromChildren": [
                60
            ],
            "childObjects": [
                {
                    "name": "qux",
                    "valToGive": "giftFromQux"
                },
                {
                    "name": "quux",
                    "valToGive": "giftFromQuux"
                }
            ]
        }
    ]
}

解决方案代码

using System.Text.Json;
using System.Text.Json.Serialization;

var json = "{\"name\":\"foo\",\"targetChildren\":[\"baz\",\"quux\"],\"valsFromChildren\":[],\"runtimeValsFromChildren\":[],\"childObjects\":[{\"name\":\"baz\",\"valToGive\":\"giftFromBaz\",\"targetChildren\":[\"qux\"],\"valsFromChildren\":[],\"runtimeValsFromChildren\":[],\"childObjects\":[{\"name\":\"qux\",\"valToGive\":\"giftFromQux\"},{\"name\":\"quux\",\"valToGive\":\"giftFromQuux\"}]}]}";
var originalObj = JsonSerializer.Deserialize<Obj>(json);
// 使用同一个Random实例避免重复值
var random = new Random();
var newObj = ProcessNestedObjects(originalObj, new List<Obj>(), random);
var newObjJson = JsonSerializer.Serialize(newObj, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(newObjJson);

// 递归处理嵌套对象,返回副本,无副作用
static Obj ProcessNestedObjects(Obj originalObj, List<Obj> ancestorCopies, Random random)
{
    // 创建当前对象的深拷贝
    var currentCopy = new Obj
    {
        Name = originalObj.Name,
        ValToGive = originalObj.ValToGive,
        TargetChildren = originalObj.TargetChildren?.ToList() ?? new List<string>(),
        // 初始化空列表,避免null引用
        ValsFromChildren = new List<string>(),
        RuntimeValsFromChildren = new List<int>(),
        ChildObjects = new List<Obj>()
    };

    // 生成当前对象的RuntimeVal并缓存,避免多次生成不同值
    var currentRuntimeVal = random.Next(0, 100);

    // 回溯所有祖辈副本,检查是否被目标包含,更新属性
    foreach (var ancestor in ancestorCopies)
    {
        if (ancestor.TargetChildren != null && ancestor.TargetChildren.Contains(currentCopy.Name))
        {
            ancestor.ValsFromChildren.Add(currentCopy.ValToGive);
            ancestor.RuntimeValsFromChildren.Add(currentRuntimeVal);
        }
    }

    // 处理子对象:创建新的祖辈列表(包含当前副本)
    var newAncestors = new List<Obj>(ancestorCopies) { currentCopy };
    if (originalObj.ChildObjects != null)
    {
        foreach (var child in originalObj.ChildObjects)
        {
            var childCopy = ProcessNestedObjects(child, newAncestors, random);
            currentCopy.ChildObjects.Add(childCopy);
        }
    }

    return currentCopy;
}

class Obj
{
    [JsonPropertyName("name")]
    public string Name { get; set; }

    [JsonPropertyName("valToGive")]
    public string ValToGive { get; set; }

    [JsonPropertyName("targetChildren")]
    public List<string> TargetChildren { get; set; }

    [JsonPropertyName("valsFromChildren")]
    public List<string> ValsFromChildren { get; set; }

    [JsonPropertyName("runtimeValsFromChildren")]
    public List<int> RuntimeValsFromChildren { get; set; }

    [JsonPropertyName("childObjects")]
    public List<Obj> ChildObjects { get; set; }
}

代码说明

  1. 无副作用实现:每次递归都创建对象的深拷贝,完全不修改原对象
  2. 跨层级回溯:递归时传递祖辈对象的副本列表,当前对象处理完成后,遍历所有祖辈检查是否在targetChildren中,更新对应属性
  3. 修复递归逻辑:遍历所有子对象并处理,不再提前返回
  4. 随机数优化:使用同一个Random实例,避免因短时间多次调用生成重复随机数,同时缓存当前对象的随机值,确保同一对象只生成一次随机数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 17:40:30