提问人:K Y 提问时间:9/27/2023 最后编辑:K Y 更新时间:9/27/2023 访问量:41
在 C# 中处理 CSV 输出的递归嵌套对象列表
Handling Recursive Nested Object Lists in C# for CSV Output
问:
我正在努力处理用于 CSV 导出的 C# 对象结构。此结构具有多层嵌套列表,其中一些可能是空的,并且它们可以是递归的。虽然我在下面展示了一个简化的模型,但我正在处理的实际结构要复杂得多。
下面是一个说明性表示:
class A
{
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public List<B> BItems { get; set; }
public List<C> CItems { get; set; }
}
class B
{
public string Name { get; set; }
public string ID { get; set; }
public D RelationshipType { get; set; }
public List<B> SubBItems { get; set; } // Recursive list
}
class C
{
public string Year { get; set; }
}
class D
{
public string Label { get; set; }
public string Value { get; set; }
}
请考虑以下示例数据:
AItems = [{ Prop1: "Code1", Prop2: "Title1", BItems: [ { Name: "Name1", ID: "ID1", RelationshipType: {Label: "Label1", Value: "Value1"}, SubBItems: [ {Name: "SubName1", ID: "SubID1", RelationshipType: {Label: "SubLabel1", Value: "SubValue1"}} ]
},
{Name: "Name2", ID: "ID2", RelationshipType: {Label: "Label2", Value: "Value2"}}
],
CItems: [{Year: "Year1"}]
}, {
Prop1: "Code2",
Prop2: "Title2",
BItems: [{Name: "Name1", ID: "ID1", RelationshipType: {Label: "Label1", Value: "Value1"}}],
CItems: [{Year: "Year1"}, {Year: "Year2"}]
}];
预期的 CSV 输出为:
Prop1, Prop2, B_Name, B_ID, D_Label, D_Value, B_SubB_Name, B_SubB_ID, D_SubB_Label, D_SubB_Value, C_Year
Code1, Title1, Name1, ID1, Label1, Value1, SubName1, SubID1, SubLabel1, SubValue1, Year1
Code1, Title1, Name2, ID2, Label2, Value2, , , , , Year1
Code2, Title2, Name1, ID1, Label1, Value1, , , , , Year1
Code2, Title2, Name1, ID1, Label1, Value1, , , , , Year2
我正在尝试生成行,但我的方法正在努力适当地合并并行列表中的对象,尤其是相同深度的列表。
public static void GenerateRows(
List<Dictionary<string, string>> records,
Dictionary<string, string> currentRow,
object obj,
string prefix,
HashSet<string> allHeaders)
{
if (obj == null) return;
var type = obj.GetType();
if (obj is IEnumerable && type != typeof(string))
{
foreach (var item in obj as IEnumerable)
{
var clonedRow = new Dictionary<string, string>(currentRow);
GenerateRows(records, clonedRow, item, prefix, allHeaders);
}
return;
}
// Process properties for the current object
ProcessProperties(records, currentRow, obj, prefix, allHeaders);
}
private static void ProcessProperties(
List<Dictionary<string, string>> records,
Dictionary<string, string> row,
object obj,
string prefix,
HashSet<string> allHeaders)
{
var type = obj.GetType();
// Create a list to temporarily hold generated rows from lists.
List<Dictionary<string, string>> tempListRows = new List<Dictionary<string, string>>();
foreach (var prop in type.GetProperties())
{
if (prop.CanRead && prop.GetIndexParameters().Length == 0)
{
var val = prop.GetValue(obj);
var propName = string.IsNullOrEmpty(prefix) ? prop.Name : $"{prefix}_{prop.Name}";
if (val is string || val is ValueType)
{
row[propName] = val?.ToString();
allHeaders.Add(propName);
}
else if (val is IEnumerable && prop.PropertyType != typeof(string))
{
List<Dictionary<string, string>> newRows = new List<Dictionary<string, string>>();
foreach (var item in val as IEnumerable)
{
var newRow = new Dictionary<string, string>();
GenerateRows(newRows, newRow, item, propName, allHeaders);
}
tempListRows.AddRange(newRows);
}
else
{
GenerateRows(records, row, val, propName, allHeaders);
}
}
}
// Merge rows
if (tempListRows.Count == 0)
{
records.Add(row);
}
else
{
foreach (var tempRow in tempListRows)
{
var mergedRow = new Dictionary<string, string>(row);
foreach (var kvp in tempRow)
{
mergedRow[kvp.Key] = kvp.Value;
}
records.Add(mergedRow);
}
}
}
任何建议将不胜感激。
答: 暂无答案
评论