[{ "EmployeeID": 1, "EmployeeName": "ABC", "Department": "US", "Level": null, "ParentID": null, "Employees": [{ "EmployeeID": 4, "EmployeeName": "ABCD", "Department": "US", "Level": null, "ParentID": 1, "Employees": [] }, { "EmployeeID": 6, "EmployeeName": "ABCDS", "Department": "US", "Level": null, "ParentID": 1, "Employees": [] }, { "EmployeeID": 7, "EmployeeName": "ABCDS", "Department": "US", "Level": null, "ParentID": 1, "Employees": [{ "EmployeeID": 8, "EmployeeName": "ABCD", "Department": "US", "Level": null, "ParentID": 7, "Employees": [] }, { "EmployeeID": 9, "EmployeeName": "ABCDS", "Department": "US", "Level": null, "ParentID": 7, "Employees": [] } ] } ] }]
The above is a JSON nested list, after serializing it into a C# List object I want to set Level
prop so that, for example Employee ID 1 will have level 1, Employee ID 4, 6, 7 will have level 2, Employee ID 8, 9 will have level 3.
This is a nested list it can have n
number of layers, I have tried few things by looping but failing, can any one please help. Below is my class object.
public class Employee { public int EmployeeID { get; set; } public string EmployeeName { get; set; } public string Department { get; set; } public int? Level { get; set; } public List<Employee> Employees { get; set; } }
Converting it to List object using below code
var jsonstring = File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "Employee.json")); var employeeList = JsonSerializer.Deserialize<List<Employee>>(jsonstring);
Answer
This is a perfect use case for recursive programming. However to avoid using the call-stack (which is limited in size), we can use a custom stack which is technically limited only by the system memory. You can do something like this:
public static class EmployeeExtensions { public static Employee PopulateLevels(this Employee e){ var employees = new Stack<Employee>(); employees.Push(e); if(e.Level == null){ e.Level = 1; } while(employees.Count > 0){ var parent = employees.Pop(); foreach(var child in parent.Employees ?? Enumerable.Empty<Employee>()){ child.Level = parent.Level + 1; employees.Push(child); } } return e; } }
Use it:
foreach(var e in employeeList){ e.PopulateLevels(); } //or inline with the ToList var employeeList = JsonSerializer.Deserialize<List<Employee>>(jsonstring) .Select(e => e.PopulateLevels()).ToList();
I would not care too much about performance if the data size is small (or even fairly large), the code above however may be improved a bit like this:
public static class EmployeeExtensions { public static Employee PopulateLevels(this Employee e){ if(e.Level == null){ e.Level = 1; } if(e.Employees == null) return e; var employees = new Stack<Employee>(); employees.Push(e); while(employees.Count > 0){ var parent = employees.Pop(); var childLevel = parent.Level + 1; foreach(var child in parent.Employees){ child.Level = childLevel; if(child.Employees?.Any() ?? false){ employees.Push(child); } } } return e; } }
Of course benchmarking is always needed to see how much it can be improved. Minor improvement is usually not worth the effort.
To flatten the employees:
public static class EmployeeExtensions { public static IEnumerable<Employee> GetAllEmployees(this Employee e){ yield return e; if(e.Employees == null) yield break; var employees = new Stack<Employee>(); employees.Push(e); while(employees.Count > 0){ var parent = employees.Pop(); foreach(var child in parent.Employees){ yield return child; if(child.Employees?.Any() ?? false){ employees.Push(child); } } } } }
Use it
var employeeList = employeeList.SelectMany(e => e.GetAllEmployees()).ToList();