I need to recurse down an object with arbitrarily deep nesting of child objects. On each level of nesting I reach, I need to refer back to any (potentially all) of the objects on levels I've already been through, i.e. any grandparents of the current child object I am at. I then need to set properties on any of those referenced parent objects based on the current object's state. The properties I'm setting is determined by JSON.
Some values will be determined at runtime (those that are randomly generated in the example below) and others will just be strings from the JSON.
Minimum reproducible example:
Take this JSON as input:
{
"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"
}
]
}
]
}
The targetChildren
refer to the name
s of the child/grandchild objects from which I want to get values and give them to "this" object. The valsFromChildren
List should be populated based on that connection - the children with the matching names from targetChildren
should put their valToGive
value in the valsFromChildren
List on the object targeting them. We can assume that there will be no duplicate name
s.
runtimeValsFromChildren
is a List that gets filled with random numbers that are computed by the child object that is giving the value to the parent.
C# console app (needs to be Dotnet version 6):
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);
}
Desired output in 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"
}
]
}
]
}
Actual output:
{
"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"
}
]
}
]
}
I need the valsFromChildren
List on the object with name foo
to include giftFromQuux
(a grand child that I want to grab from the 3rd level down). At the moment it only manages to get the value from its immediate child ("baz"), not its grand child ("quux"). It needs to be a recursive solution that should work for grandparents n levels down the nesting.
I would also like to know how to not mutate the original object but instead return a copy at a different memory address, i.e. have the method not have side effects.
Thanks.