Traverse()
中存在一个错误,导致它多次迭代节点。
public IEnumerable<HtmlNode> Traverse()
{
foreach (var node in _context)
{
yield return node;
foreach (var child in Children().Traverse())
yield return child;
}
}
public SharpQuery Children()
{
return new SharpQuery(_context.SelectMany(n => n.ChildNodes).Where(n => n.NodeType == HtmlNodeType.Element), this);
}
public SharpQuery(IEnumerable<HtmlNode> nodes, SharpQuery previous = null)
{
if (nodes == null) throw new ArgumentNullException("nodes");
_previous = previous;
_context = new List<HtmlNode>(nodes);
}
static void Main(string[] args)
{
var sq = new SharpQuery(@"
<a>
<b>
<c/>
<d/>
<e/>
<f>
<g/>
<h/>
<i/>
</f>
</b>
</a>");
var nodes = sq.Traverse();
Console.WriteLine("{0} nodes: {1}", nodes.Count(), string.Join(",", nodes.Select(n => n.Name)));
Console.ReadLine();
19个节点:#document,a,b,c,g,h,i,d,g,h,i,e,g,h,i,f,g,h,i
每封字母a-i打印一次。
似乎无法弄清楚它出错的地方...... node.ChildNodes
确实只返回直接的孩子,对吧? (来自HtmlAgilityPack)
全班(浓缩),如果你想尝试自己运行它。
public class SQLite
{
private readonly List<HtmlNode> _context = new List<HtmlNode>();
private readonly SQLite _previous = null;
public SQLite(string html)
{
var doc = new HtmlDocument();
doc.LoadHtml(html);
_context.Add(doc.DocumentNode);
}
public SQLite(IEnumerable<HtmlNode> nodes, SQLite previous = null)
{
if (nodes == null) throw new ArgumentNullException("nodes");
_previous = previous;
_context = new List<HtmlNode>(nodes);
}
public IEnumerable<HtmlNode> Traverse()
{
foreach (var node in _context)
{
yield return node;
foreach (var child in Children().Traverse())
yield return child;
}
}
public SQLite Children()
{
return new SQLite(_context.SelectMany(n => n.ChildNodes).Where(n => n.NodeType == HtmlNodeType.Element), this);
}
}
答案 0 :(得分:4)
首先,请注意,只要我们迭代没有任何兄弟的元素,一切都按计划进行。一旦我们点击元素<c>
,事情就会开始变得混乱。同样有趣的是<c>
,<d>
和<e>
都认为它们包含<f>
的孩子。
让我们仔细看看您对SelectMany()
的电话:
_context.SelectMany(n => n.ChildNodes)
遍历_context
中的项目,累积每个项目的子节点。我们来看看_context
。一切都应该没问题,因为它是List
长度1
。或者是吗?
我怀疑你的SharpQuery(string)
构造函数将兄弟元素存储在同一个列表中。在这种情况下,_context
可能不再是1
长度,请记住,SelectMany()
累积列表中每个项目的子节点。
例如,如果_context
是包含<c>
,<d>
,<e>
和<f>
的列表,则只有<f>
有子女, SelectMany()
会为每个元素调用一次,它会累积并返回<f>
个子元素四次。
我认为这是你的错误。
编辑:由于您已发布完整课程,因此我不必再猜测了。迭代<b>
(迭代器替换为列表以便更好地理解)时,请查看操作序列:
Children()
上的<b>
,其中<c>
,<d>
,<e>
和<f>
,Traverse()
:
Children()
,上致电<c>
,但 _context
实际上包含<c>
,<d>
,<e>
和{ {1}},不仅<f>
,还有<c>
,<g>
和<h>
,<i>
上致电Children()
,同样的事情,因为<d>
_context
和{ {1}}(以及<c>
和<d>
),答案 1 :(得分:3)
儿童()被打破,它也选择了兄弟姐妹的孩子。我会改写成这样的东西:
public IEnumerable<HtmlNode> Traverse(IEnumerable<HtmlNode> nodes)
{
foreach (var node in nodes)
{
yield return node;
foreach (var child in Traverse(ChildNodes(node)))
yield return child;
}
}
private IEnumerable<HtmlNode> ChildNodes(HtmlNode node)
{
return node.ChildNodes.Where(n => n.NodeType == HtmlNodeType.Element);
}
public SharpQuery(IEnumerable<HtmlNode> nodes, SharpQuery previous = null)
{
if (nodes == null) throw new ArgumentNullException("nodes");
_previous = previous; // Is this necessary?
_context = new List<HtmlNode>(nodes);
}
答案 2 :(得分:2)
这不应该足够吗?
public IEnumerable<HtmlNode> Traverse()
{
foreach (var node in _context)
{
Console.WriteLine(node.Name);
yield return node;
foreach (var child in Children().Traverse())
{} //yield return child;
}
}
答案 3 :(得分:1)
我无法确切地说出来,但你可以看到模式是,一旦你的东西遇到“c”的“/”,它开始考虑“g,h,i”成为后续每一个的一部分直到它遇到“f”的“/”。
很可能你有一个额外的循环,你应该骑。
或者出于某种原因,它无法正确计算“/&gt;”部分。
答案 4 :(得分:1)
我会做这样的事情,看看它是否解决了这个问题:
public IEnumerable<HtmlNode> Traverse()
{
foreach (var node in _context)
{
Console.WriteLine(node.Name);
if(!node.HasChildren) {
yield return node;
}
else {
foreach (var child in Children().Traverse())
yield return child;
}
}
}
}
答案 5 :(得分:0)
public IEnumerable<HtmlNode> All()
{
var queue = new Queue<HtmlNode>(_context);
while (queue.Count > 0)
{
var node = queue.Dequeue();
yield return node;
foreach (var next in node.ChildNodes.Where(n => n.NodeType == HtmlNodeType.Element))
queue.Enqueue(next);
}
}