使用LINQ从HashSet值中检查父级的后代

时间:2016-09-08 18:14:30

标签: c# linq

我有一个HashSet,包含我要检索的xml子节点。我使用以下linq(不工作)尝试获取所有孩子并返回父母:

的xml:

<team>
    <bob>a</bob>
    <mary>b</mary>
    <joe>c</joe>
</team>
<team>
    <john>d</john>
</team>

HashSet的:

HashSet<string> people = new HashSet<string> { "mary", "joe", "john" };

LINQ:

IEnumerable<XElement> result = from d in doc.Descendants("team")
                               where people.Contains(d.Descendants().Name.LocalName)
                               select d;

应该回归:

<team>
    <mary>b</mary>
    <joe>c</joe>
</team>
<team>
    <john>d</john>
</team>

但它不起作用。任何帮助,将不胜感激。感谢。

更新#1

我正在添加一个完整的示例,它不返回任何内容:

public void test1()
        {
            var xml =
                     @"<?xml version=""1.0"" encoding=""utf-8""?>
                       <hereistheroot>
                           <team>
                               <bob>a</bob>
                               <mary>b</mary>
                               <joe>c</joe>
                           </team>
                           <team>
                               <john>d</john>
                           </team>
                       </hereistheroot>";
            XElement doc = XElement.Parse(xml);

            HashSet<string> people = new HashSet<string> { "mary", "joe", "john" };

            IEnumerable<XElement> result = from d in doc.Descendants("team")
                                           where people.Contains(d.Name.LocalName)
                                           select d;

        }

它什么都不返回。它应该返回两个XElement对象:

第一个:

<team>
    <mary>b</mary>
    <joe>c</joe>
</team>

第二个:

<team>
    <john>d</john>
</team>

注意:为了测试,删除linq中的“Where”子句,它将正确返回两个XElement对象,但这是主要问题:我需要获取“Where”子句允许我根据HashSet选择人员。感谢。

1 个答案:

答案 0 :(得分:1)

在我看来,你实际上是想在这里完成两件不同的事情:

  1. 标识包含具有您的集合中名称的子节点的所有顶级team元素。
  2. 构造一个仅包含那些顶级team元素的新XML DOM,并且在这些元素中只有那些具有您的集合中名称的子节点。
  3. 如果这是正确的,那么以下内容将实现:

    IEnumerable<XElement> result = from d in doc.Descendants("team")
                                   let validDescendants = d.Descendants()
                                       .Where(c => people.Contains(c.Name.LocalName)).ToArray()
                                   where validDescendants.Length > 0
                                   select new XElement(d.Name, validDescendants);
    

    使用更多查询表达式语法(即初始化validDescendants)的上述替代方法:

    IEnumerable<XElement> result = from d in doc.Descendants("team")
                                   let validDescendants =
                                        (from c in d.Descendants()
                                         where people.Contains(c.Name.LocalName)
                                         select c).ToArray()
                                   where validDescendants.Length > 0
                                   select new XElement(d.Name, validDescendants);
    

    请注意,此方法保持原始doc树不变,而是返回具有所需配置的全新team元素的枚举。

    如果您只是保留doc树并从已返回的team元素中删除子元素,则可以执行以下操作:

    IEnumerable<XElement> result = from d in doc.Descendants("team")
                                   where d.Descendants()
                                       .Any(c => people.Contains(c.Name.LocalName))
                                   select d;
    
    foreach (XElement element in result)
    {
        foreach (XElement child in element.Descendants().ToList())
        {
            if (!people.Contains(child.Name.LocalName))
            {
                child.Remove();
            }
        }
    }
    

    当然,在该示例中,它将保留包含至少一个在您的集合中具有名称的元素的任何team元素。当然,这也是可以修复的,但是因为你似乎只想要在过滤后检索非空的元素,所以我假设上面的第一个例子更符合你想要的。我将第二个仅作为变体展示,如果你有更具体的东西,你没有在你的问题中完全描述,那么你可以继续使用。