node.Descendants (0), кажется, возвращает все дочерние узлы вместо первого уровня - PullRequest
0 голосов
/ 05 июля 2018

Я использую HtmlAgilityPack для перемещения по дереву документов по одному уровню за раз. Однако кажется, что вызов node.Descendants(0) возвращает все дерево узлов.

Примечание: я попытался вставить в мою дословную строку HTML, но парсеру SE это не понравилось, поэтому я добавил его как фрагмент кода.

<html>
    <head>
    <meta name="generator"
    content="HTML Tidy for HTML5 (experimental) for Windows https://github.com/w3c/tidy-html5/tree/c63cc39" />
    <title></title>
    </head>
    <body>
    <p id="p1" class="newline">
        <span id="span1" class="bold">
        <span id="span2" class="literal">BOLD TEXT</span>
        </span>
    </p>
    </body>
</html>
var doc = new HtmlAgilityPack.HtmlDocument();

doc.LoadHtml(html);

var lines = doc.DocumentNode.Descendants().Where(x => x.HasClass("newline")).ToArray();

Console.WriteLine(string.Join("\r\n", lines[0].Descendants(0)
    .Select(x => $"{x.Name} {x.Id} {(x as HtmlTextNode)?.Text}")));

Код, приведенный выше, получает потомков первого тега p. Если я передаю 0 или 1 в качестве аргумента, он возвращает все дерево узлов и выводит их ниже. Дело в том, что текстовый узел, содержащий BOLD TEXT, вложен на 3 уровня ниже тега p. С приведенным выше кодом я бы ожидал, что он вернет текстовый узел span1, а затем еще один текстовый узел.

Что я делаю не так в своем звонке на .Descendants?

#text

span span1
#text

span span2
#text  BOLD TEXT
#text

#text

Редактировать: Временный обходной путь - убедиться, что вы получаете только потомков, у которых родительский элемент равен текущему узлу. Однако все еще ищу более практичное решение.

Console.WriteLine(string.Join("\r\n", lines[0].Descendants(0)
    .Where(x => x.ParentNode == lines[0])
    .Select(x => $"{x.Name} {x.Id} {(x as HtmlTextNode)?.Text}")));

1 Ответ

0 голосов
/ 24 июля 2018

У меня возникла та же проблема, я начал гуглить и нашел твой вопрос :). И тогда я решил обратиться к разработчикам напрямую . А вот краткий вариант ответа:

Согласно коду он ведет себя по-другому:

/// <summary>
/// Gets all Descendant nodes in enumerated list
/// </summary>
/// <returns></returns>
public IEnumerable<HtmlNode> Descendants(int level)
{
    if (level > HtmlDocument.MaxDepthLevel)
    {
        throw new ArgumentException(HtmlNode.DepthLevelExceptionMessage);
    }

    foreach (HtmlNode node in ChildNodes)
    {
        yield return node;

        foreach (HtmlNode descendant in node.Descendants(level + 1))
        {
            yield return descendant;
        }
    }
}

Он принимает всех потомков и дочерних элементов, увеличивая уровень на единицу до тех пор, пока не останется больше потомков или не будет достигнут максимальный уровень (int.MaxValue). Однако, я согласен с вами, он, вероятно, должен возвращать потомка, пока не будет достигнут указанный уровень. К сожалению, для обратной совместимости мы, вероятно, ничего не сделаем с этим методом, чтобы не повлиять на текущие приложения.

Однако, в вашем случае ChildNodes может использоваться вместо Descendants(0). Код будет выглядеть так:

    Console.WriteLine(string.Join("\r\n", lines[0].ChildNodes
                            .Select(x => $"{x.Name} {x.Id} {(x as HtmlTextNode)?.Text}")));
...