我正在使用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);
}
但是如果textnode不是父节点的唯一子节点,这似乎会导致问题。例如:
<label>Email:<br><input name="txtID" type="text" id="txtID" class="input"></label>
更换后,访问doc.DocumentNode.OuterHTML会导致以下异常: 无法将“HtmlAgilityPack.HtmlNode”类型的对象强制转换为“HtmlAgilityPack.HtmlTextNode”。
我是否错误地进行了更换?我无法真正去“清理”可能贯穿此内容的所有原始HTML文档。
答案 0 :(得分:5)
这似乎是您使用的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);
}
这将正确替换您的文本节点。