递归映射问题
Recursion mapping issue
我有一个自引用第 n 深度结构。我想将这个结构映射到一个字符串列表中。但是我似乎没有成功。
public class SPFolderStructure
{
public string Name { get; set; }
public List<SPFolderStructure> Structure { get; set; }
}
所以结构可以是这样的:
[
{
"Name": "General",
"Structure": [
{
"Name": "Important",
"Structure": [
{
"Name": "Readings",
"Structure": []
},
{
"Name": "Pictures",
"Structure": []
},
{
"Name": "Support",
"Structure": []
},
{
"Name": "RandomStuff",
"Structure": []
},
{
"Name": "Money",
"Structure": []
},
{
"Name": "Gifs",
"Structure": []
},
{
"Name": "Cells",
"Structure": []
}
]
}
]
},
{
"Name": "Alert",
"Structure": [
{
"Name": "Important",
"Structure": [
{
"Name": "Readings",
"Structure": []
},
{
"Name": "Pictures",
"Structure": []
},
{
"Name": "Support",
"Structure": []
},
{
"Name": "RandomStuff",
"Structure": []
},
{
"Name": "Money",
"Structure": []
},
{
"Name": "Gifs",
"Structure": []
},
{
"Name": "Cells",
"Structure": []
}
]
}
]
}
]
结果应该是 14 个字符串
/General/Important/Readings
/General/Important/Pictures
/General/Important/Support
/General/Important/RandomStuff
/General/Important/Money
/General/Important/Gifs
/General/Important/Cells
/Alert/Important/Readings
/Alert/Important/Pictures
/Alert/Important/Support
/Alert/Important/RandomStuff
/Alert/Important/Money
/Alert/Important/Gifs
/Alert/Important/Cells
我尝试过递归,在简单的结构上取得了小成功,但在像这样的复杂结构上,运气不好。
public static void DecodeFolderStructure(List<SPFolderStructure> list, List<string> result, string baseName = null, string fullPath = null) {
for (var i = 0; i != list.Count; i++) {
if (string.IsNullOrEmpty(fullPath) && !string.IsNullOrEmpty(baseName)) {
fullPath += $"/{baseName}";
}
var item = list[i];
item.Name = item.Name.Trim();
if (baseName != item.Name) {
fullPath += $"/{item.Name}";
}
if (item.Structure is {Count: > 0}) {
DecodeFolderStructure(item.Structure, result, baseName, fullPath);
fullPath = null;
}
if (fullPath == null) {
continue;
}
result.Add(fullPath);
fullPath = null;
}
}
您的结构很好,而且您创建的 class 与 JSON 字符串完全匹配。您可以简单地将字符串反序列化为一个对象。接下来你需要递归来打印出结果,如下所示。
注意:我在您的 class 中使用了 ToString() 的覆盖作为输出。您不必这样做,但在调试时也非常方便。
string json = @"[
{
'Name': 'General',
'Structure': [
{
'Name': 'Important',
'Structure': [
{
'Name': 'Readings',
'Structure': []
},
{
'Name': 'Pictures',
'Structure': []
},
{
'Name': 'Support',
'Structure': []
},
{
'Name': 'RandomStuff',
'Structure': []
},
{
'Name': 'Money',
'Structure': []
},
{
'Name': 'Gifs',
'Structure': []
},
{
'Name': 'Cells',
'Structure': []
}
]
}
]
},
{
'Name': 'Alert',
'Structure': [
{
'Name': 'Important',
'Structure': [
{
'Name': 'Readings',
'Structure': []
},
{
'Name': 'Pictures',
'Structure': []
},
{
'Name': 'Support',
'Structure': []
},
{
'Name': 'RandomStuff',
'Structure': []
},
{
'Name': 'Money',
'Structure': []
},
{
'Name': 'Gifs',
'Structure': []
},
{
'Name': 'Cells',
'Structure': []
}
]
}
]
}
]";
// Deserializing the string into a list of SPFolderStructure objects.
List<SPFolderStructure> result = Newtonsoft.Json.JsonConvert.DeserializeObject<List<SPFolderStructure>>(json);
// Now call the recursive routine to print out the result.
DecodeFolderStructure(string.Empty, result);
void DecodeFolderStructure(string parent, List<SPFolderStructure> list)
{
if(list != null)
foreach (SPFolderStructure item in list)
{
var dir = $"{parent}{item}";
Console.WriteLine(dir);
// Recursion here. Call your own routine again.
DecodeFolderStructure(dir, item.Structure);
}
}
Console.ReadKey();
public class SPFolderStructure
{
public string Name { get; set; }
public List<SPFolderStructure> Structure { get; set; }
public override string ToString()
{
return $"/{Name}";
}
}
结果如下:
/General
/General/Important
/General/Important/Readings
/General/Important/Pictures
/General/Important/Support
/General/Important/RandomStuff
/General/Important/Money
/General/Important/Gifs
/General/Important/Cells
/Alert
/Alert/Important
/Alert/Important/Readings
/Alert/Important/Pictures
/Alert/Important/Support
/Alert/Important/RandomStuff
/Alert/Important/Money
/Alert/Important/Gifs
/Alert/Important/Cells
您可以使用带递归的 LINQ 来转换每个级别:
public static class ListExt {
public static List<string> ToStrings(this List<SPFolderStructure> src)
=> src.SelectMany(s => s.Structure.Count > 0
? s.Structure.ToStrings().Select(s2 => $"/{s.Name}/{s2}")
: new[] { $"/{s.Name}" })
.ToList();
}
这是一种方法。它使用局部函数来隐藏路径为 'accumulator' 的递归部分。它只有 returns 每个列表的叶节点路径:
private static IEnumerable<string> DecodeFolderStructure(IEnumerable<SPFolderStructure> structures)
{
return structures.SelectMany(s => GetPaths(s, Enumerable.Empty<string>()));
IEnumerable<string> GetPaths(SPFolderStructure structure, IEnumerable<string> path)
{
path = path.Concat(new[] { structure.Name });
return structure.Structure.Any()
? structure.Structure.SelectMany(s => GetPaths(s, path))
: new[] { string.Join("/", path) };
}
}
有关工作演示,请参阅 this fiddle。
我有一个自引用第 n 深度结构。我想将这个结构映射到一个字符串列表中。但是我似乎没有成功。
public class SPFolderStructure
{
public string Name { get; set; }
public List<SPFolderStructure> Structure { get; set; }
}
所以结构可以是这样的:
[
{
"Name": "General",
"Structure": [
{
"Name": "Important",
"Structure": [
{
"Name": "Readings",
"Structure": []
},
{
"Name": "Pictures",
"Structure": []
},
{
"Name": "Support",
"Structure": []
},
{
"Name": "RandomStuff",
"Structure": []
},
{
"Name": "Money",
"Structure": []
},
{
"Name": "Gifs",
"Structure": []
},
{
"Name": "Cells",
"Structure": []
}
]
}
]
},
{
"Name": "Alert",
"Structure": [
{
"Name": "Important",
"Structure": [
{
"Name": "Readings",
"Structure": []
},
{
"Name": "Pictures",
"Structure": []
},
{
"Name": "Support",
"Structure": []
},
{
"Name": "RandomStuff",
"Structure": []
},
{
"Name": "Money",
"Structure": []
},
{
"Name": "Gifs",
"Structure": []
},
{
"Name": "Cells",
"Structure": []
}
]
}
]
}
]
结果应该是 14 个字符串
/General/Important/Readings
/General/Important/Pictures
/General/Important/Support
/General/Important/RandomStuff
/General/Important/Money
/General/Important/Gifs
/General/Important/Cells
/Alert/Important/Readings
/Alert/Important/Pictures
/Alert/Important/Support
/Alert/Important/RandomStuff
/Alert/Important/Money
/Alert/Important/Gifs
/Alert/Important/Cells
我尝试过递归,在简单的结构上取得了小成功,但在像这样的复杂结构上,运气不好。
public static void DecodeFolderStructure(List<SPFolderStructure> list, List<string> result, string baseName = null, string fullPath = null) {
for (var i = 0; i != list.Count; i++) {
if (string.IsNullOrEmpty(fullPath) && !string.IsNullOrEmpty(baseName)) {
fullPath += $"/{baseName}";
}
var item = list[i];
item.Name = item.Name.Trim();
if (baseName != item.Name) {
fullPath += $"/{item.Name}";
}
if (item.Structure is {Count: > 0}) {
DecodeFolderStructure(item.Structure, result, baseName, fullPath);
fullPath = null;
}
if (fullPath == null) {
continue;
}
result.Add(fullPath);
fullPath = null;
}
}
您的结构很好,而且您创建的 class 与 JSON 字符串完全匹配。您可以简单地将字符串反序列化为一个对象。接下来你需要递归来打印出结果,如下所示。
注意:我在您的 class 中使用了 ToString() 的覆盖作为输出。您不必这样做,但在调试时也非常方便。
string json = @"[
{
'Name': 'General',
'Structure': [
{
'Name': 'Important',
'Structure': [
{
'Name': 'Readings',
'Structure': []
},
{
'Name': 'Pictures',
'Structure': []
},
{
'Name': 'Support',
'Structure': []
},
{
'Name': 'RandomStuff',
'Structure': []
},
{
'Name': 'Money',
'Structure': []
},
{
'Name': 'Gifs',
'Structure': []
},
{
'Name': 'Cells',
'Structure': []
}
]
}
]
},
{
'Name': 'Alert',
'Structure': [
{
'Name': 'Important',
'Structure': [
{
'Name': 'Readings',
'Structure': []
},
{
'Name': 'Pictures',
'Structure': []
},
{
'Name': 'Support',
'Structure': []
},
{
'Name': 'RandomStuff',
'Structure': []
},
{
'Name': 'Money',
'Structure': []
},
{
'Name': 'Gifs',
'Structure': []
},
{
'Name': 'Cells',
'Structure': []
}
]
}
]
}
]";
// Deserializing the string into a list of SPFolderStructure objects.
List<SPFolderStructure> result = Newtonsoft.Json.JsonConvert.DeserializeObject<List<SPFolderStructure>>(json);
// Now call the recursive routine to print out the result.
DecodeFolderStructure(string.Empty, result);
void DecodeFolderStructure(string parent, List<SPFolderStructure> list)
{
if(list != null)
foreach (SPFolderStructure item in list)
{
var dir = $"{parent}{item}";
Console.WriteLine(dir);
// Recursion here. Call your own routine again.
DecodeFolderStructure(dir, item.Structure);
}
}
Console.ReadKey();
public class SPFolderStructure
{
public string Name { get; set; }
public List<SPFolderStructure> Structure { get; set; }
public override string ToString()
{
return $"/{Name}";
}
}
结果如下:
/General
/General/Important
/General/Important/Readings
/General/Important/Pictures
/General/Important/Support
/General/Important/RandomStuff
/General/Important/Money
/General/Important/Gifs
/General/Important/Cells
/Alert
/Alert/Important
/Alert/Important/Readings
/Alert/Important/Pictures
/Alert/Important/Support
/Alert/Important/RandomStuff
/Alert/Important/Money
/Alert/Important/Gifs
/Alert/Important/Cells
您可以使用带递归的 LINQ 来转换每个级别:
public static class ListExt {
public static List<string> ToStrings(this List<SPFolderStructure> src)
=> src.SelectMany(s => s.Structure.Count > 0
? s.Structure.ToStrings().Select(s2 => $"/{s.Name}/{s2}")
: new[] { $"/{s.Name}" })
.ToList();
}
这是一种方法。它使用局部函数来隐藏路径为 'accumulator' 的递归部分。它只有 returns 每个列表的叶节点路径:
private static IEnumerable<string> DecodeFolderStructure(IEnumerable<SPFolderStructure> structures)
{
return structures.SelectMany(s => GetPaths(s, Enumerable.Empty<string>()));
IEnumerable<string> GetPaths(SPFolderStructure structure, IEnumerable<string> path)
{
path = path.Concat(new[] { structure.Name });
return structure.Structure.Any()
? structure.Structure.SelectMany(s => GetPaths(s, path))
: new[] { string.Join("/", path) };
}
}
有关工作演示,请参阅 this fiddle。