2013-03-30 100 views
1

我正在使用HTMLAgilityPack解析某些html。我正在使用我期望的结果集,并将xpath查询与linq查询结合使用。有没有一种方法可以将它们组合成一个LINQ查询?将这两个表达式组合成单个LINQ表达式

var test = doc.DocumentNode.SelectNodes("//div[@class='product']"); 

foreach (var item in test) 
{ 

    var result = from input in item.Descendants("span") 
       where input.Attributes["class"].Value == "Bold" 
       where input.InnerHtml.ToUpper() == "GetItem" 
       select input; 

    return result;   
} 
+1

为什么你会要吗?它是否提高可读性或性能?我对此表示怀疑。 – Maarten

+0

顺便说一句,你从每个循环中返回结果,所以只处理'test'中的第一项。 – Maarten

+0

@Maarten它可以提高性能是的。它与你的foreach/List.Add评论有关。如果调用代码只是添加.Any()或.FirstOrDefault()或.Take(),则枚举器将提前中断,而不是贯穿整个XML文档。 – Aron

回答

1
return (
    from result in 
     from item in doc.DocumentNode.SelectNodes("//div[@class='product']") 
     from input in item.Descendants("span") 
     where input.Attributes["class"].Value == "Bold" 
     where input.InnerHtml.ToUpper() == "GetItem" 
     select input 
    select result 
    ).First(); 
+0

严格来说这是正确的。但我怀疑.First首先来自OP代码中的一个错误。 – Aron

+0

@Aron:我看到foreach循环直接返回结果,所以.. –

+0

过早返回将删除对foreach的需求,因此我怀疑它有一个bug。 – Aron

1

如果你真的希望这一切在一个查询,你可以像这样的东西:

var result = doc.DocumentNode.SelectNodes("//div[@class='product']") 
    .SelectMany(e => e.Descendants("span") 
     .Where(x => x.Attributes["class"].Value == "Bold" && 
        x.InnerHtml.ToUpper() == "GetItem")) 
    .ToList(); 

return result; 

我会建议间隔出来有点但对于可读性的原因,更多的东西是这样的:

var result = new List<SomeType>(); 
var nodes = doc.DocumentNode.SelectNodes("//div[@class='product']"); 

nodes.SelectMany(e => e.Descendants("span") 
    .Where(x => x.Attributes["class"].Value == "Bold" && 
       x.InnerHtml.ToUpper() == "GetItem")); 

return result.ToList(); 

SelectMany()方法将变平的内查询的结果到一个单一的IEnumerable<T>

+1

个人观点:我真的不喜欢使用ForEach,在其中使用someList.Add(...)填充列表。请将其更改为Select(...)以将其投影到想要的类型中,并最终使用ToList()并将其直接存储在结果变量中。 – Maarten

2

如果你想所有的跨度聚集在一起(如果我的假设,正确的是你想要的)......

我会先将其转换为一个更流畅的符号(我觉得SelectMany这种方式更容易 - 但这只是我)

(免责声明:我从内存中写这篇文章,复制/粘贴你的代码 - 目前不是VS - 你需要检查,使它如果有任何问题,请写 - 但我认为我可以或多或少)

var test = doc.DocumentNode.SelectNodes("//div[@class='product']"); 
foreach(var item in test) 
    item.Descendants("span").Where(input => input.Attributes["class"].Value == "Bold").Where(input => input.InnerHtml.ToUpper() == "GetItem").Select(input => input); 

终于...

var allSpans = doc.DocumentNode.SelectNodes("//div[@class='product']") 
    .SelectMany(item => item.Descendants("span").Where(input => input.Attributes["class"].Value == "Bold").Where(input => input.InnerHtml.ToUpper() == "GetItem")); 

...或者沿着这些线路

+0

SelectMany也是我的投票。 – oscilatingcretin

2

只是想告诉你其他的方式做在的SelectMany Linq的。这是一种风格选择,因此SO中的许多人更喜欢.SelectMany扩展方法,因为他们可以看到Monad如何应用于IEnumerable。我更喜欢这种方法,因为它更接近函数式编程模型。

return from product in doc.DocumentNode.SelectNodes("//div[@class='product']") 
     from input in product.Descendants("span") 
     where input.Attributes["class"].Value == "Bold" 
     where input.InnerHtml.ToUpper() == "GetItem" 
     select input;   
+0

啊..不一样.. –