SelectMany以展平嵌套结构

我正在解析XML结构,我的类看起来像如下:

class MyXml { //... List Content { get; set; } //... } class Node { // ... public List Nodes { get; set; } public string Type { get; set; } //... } 

MyXml表示我正在解析的XML文件,其元素都称为 。 每个节点都有一个type属性,可以有不同的值。

节点的类型未与其深度相关联。 我可以在任何深度级别拥有任何节点类型。

我可以正确地解析结构,所以我得到一个MyXml对象,其内容是节点列表,其中List中的节点可以有子节点等等(我使用了递归)。

我需要做的是展平整个结构并仅提取某种类型的节点。

我尝试过:

 var query = MyXml.Content.SelectMany(n => n.Nodes); 

但是它只采用结构深度为1的节点。我想在同一个集合中抓取每个节点,无论深度如何,然后过滤我需要的东西。

这是一个自然递归的问题。 使用递归lambda,尝试类似于:

 Func> flattener = null; flattener = n => new[] { n } .Concat(n.Nodes == null ? Enumerable.Empty() : n.Nodes.SelectMany(flattener)); 

请注意,当您像这样制作递归Func ,必须首先单独声明Func ,并将其设置为null。

您还可以使用迭代器块方法展平列表:

 public static IEnumerable Flatten(Node node) { yield return node; if (node.Nodes != null) { foreach(var child in node.Nodes) foreach(var descendant in Flatten(child)) yield return descendant; } } 

无论哪种方式,一旦树被展平,您可以在展平列表上执行简单的Linq查询以查找节点:

 flattener(node).Where(n => n.Type == myType); 

响应改编自: https : //stackoverflow.com/a/17086572/1480391

您应该实现Node.GetFlattened方法,该方法返回节点本身,然后在所有子节点上调用自身:

 public IEnumerable GetFlattened() { yield return this; foreach (var node in this.Nodes.SelectMany(n => n.GetFlattened())) yield return node; } 

然后,您可以调用此方法,并以递归方式返回所有节点,而不管其深度如何。 这是深度优先搜索,如果您想要广度优先搜索,则必须尝试其他方法。

 class MyXml { public List AllNodes() { List allNodes = new List(); foreach (var node in Content) AddNode(node, nodes); } public void AddNode(Node node, List nodes) { nodes.Add(node); foreach (var childNode in node.Nodes) AddNode(childNode, nodes); } public List AllNodesOfType(NodeType nodeType) { return AllNodes().Where(n => n.NodeType == nodeType); } } 

首先使用函数和查询对列表进行展平。