HTMLAgility Pack: замена содержимого в узле «смешанного типа» - PullRequest
1 голос
/ 16 марта 2012

Я использую HTMLAgility Pack для внесения некоторых изменений в вывод HTML-кода - нахождение всех текстовых узлов и их замена:

const string xpath = "//*[not(self::script or self::style)]/text()[normalize-space(.) != '']";
var docNodes = doc.DocumentNode.SelectNodes(xpath).ToList();
foreach (var htmlNode in nodes)
{
    var parent = htmlNode.ParentNode;
    var newNode = new HtmlNode(HtmlNodeType.Text, doc, 0){InnerHTML = "Test"};
    parent.ReplaceChild(newNode, htmlNode);
}                

Но это, кажется, вызывает проблему, если текстовый узел не является единственным потомком родителя. Например:

<label>Email:<br><input name="txtID" type="text" id="txtID" class="input"></label>

После замены доступ к doc.DocumentNode.OuterHTML приводит к следующему исключению: Невозможно привести объект типа 'HtmlAgilityPack.HtmlNode' к типу 'HtmlAgilityPack.HtmlTextNode'.

Я делаю замену неправильно? Я не могу пойти и "очистить" все исходные документы HTML, которые могут пройти через это.

1 Ответ

5 голосов
/ 17 марта 2012

Похоже, это проблема с несоответствием используемого вами конструктора HtmlNode(HtmlNodeType, HtmlDocument, int) и способа работы методов InnerHtml и InnerText. Конструктор HtmlNode создает узел типа HtmlNode (но устанавливает тип узла равным переданному значению). Если вы хотите получить InnerHtml или InnerText этого узла, AgilityPack выполняет что-то вроде этого:

case HtmlNodeType.Text:
    html = ((HtmlTextNode)this).Text;

, что на самом деле вызывает InvalidCastException, о котором вы упомянули.

Чтобы избежать этого, я рекомендую использовать другой способ создания текстовых узлов с помощью HtmlDocument.CreateTextNode() метода:

foreach (var htmlNode in nodes)
{
    var parent = htmlNode.ParentNode;
    var newNode = doc.CreateTextNode();
    newNode.InnerHtml = "Test";
    parent.ReplaceChild(newNode, htmlNode);
}

Это правильно заменит ваши текстовые узлы.

...