递归映射问题

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 个字符串

我尝试过递归,在简单的结构上取得了小成功,但在像这样的复杂结构上,运气不好。

        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