如何在C#中比较TinyMce与DOM返回的等效HTML字符串?
用C#实现等效HTML内容比较的正确姿势
首先得说,绝对不要用正则来处理HTML比较——HTML是嵌套的结构化文档,正则根本没法覆盖所有边缘情况(比如嵌套标签、不同的属性顺序、自闭合标签差异等等),用正则迟早会踩坑。
最靠谱的方案是用专业的HTML解析库把两个字符串解析成DOM树,然后做标准化处理,再递归比较节点结构和内容。C#里最常用的就是HtmlAgilityPack,下面给你一步步讲怎么实现:
步骤1:安装HtmlAgilityPack
先通过NuGet安装这个包,在Visual Studio里右键项目→管理NuGet程序包,搜索HtmlAgilityPack安装就行。
步骤2:解析HTML字符串到DOM对象
把两个HTML字符串分别加载进HtmlDocument对象:
string tinyMceHtml = "<p>Test<img src=\"../../../files/a54f58ca-56e1-4e6a-acf5-1cccebe51dc1/FY17/img/21c17e0e-b433-42de-97c7-cf0194fc7ac4\" /></p>"; string domHtml = "<p>Test<img src=\"/files/a54f58ca-56e1-4e6a-acf5-1cccebe51dc1/FY17/img/21c17e0e-b433-42de-97c7-cf0194fc7ac4\"></p>"; var doc1 = new HtmlAgilityPack.HtmlDocument(); doc1.LoadHtml(tinyMceHtml); var doc2 = new HtmlAgilityPack.HtmlDocument(); doc2.LoadHtml(domHtml);
步骤3:标准化处理DOM
这一步是关键,要消除那些“语义无关”的差异:比如路径格式(相对→绝对)、空白文本节点、自闭合标签格式等。
3.1 统一图片路径格式
把相对路径转成绝对路径(或者反过来,根据你的网站根目录调整):
private static void NormalizeImageSrc(HtmlNode node, string baseWebsiteUrl) { if (node.NodeType == HtmlNodeType.Element && node.Name.Equals("img", StringComparison.OrdinalIgnoreCase)) { var srcAttr = node.Attributes["src"]; if (srcAttr != null) { // 把相对路径转为网站根目录的绝对路径 Uri absoluteUri = new Uri(new Uri(baseWebsiteUrl), srcAttr.Value); srcAttr.Value = absoluteUri.AbsolutePath; } } // 递归处理所有子节点 foreach (HtmlNode child in node.ChildNodes.ToList()) { NormalizeImageSrc(child, baseWebsiteUrl); } } // 假设你的网站根URL是"https://your-site.com/" NormalizeImageSrc(doc1.DocumentNode, "https://your-site.com/"); NormalizeImageSrc(doc2.DocumentNode, "https://your-site.com/");
3.2 移除无关的空白文本节点
DOM和TinyMce返回的HTML可能包含多余的换行、空格,这些不影响语义,直接删掉:
private static void RemoveWhitespaceTextNodes(HtmlNode node) { var childNodes = node.ChildNodes.ToList(); foreach (HtmlNode child in childNodes) { if (child.NodeType == HtmlNodeType.Text && string.IsNullOrWhiteSpace(child.InnerText)) { node.RemoveChild(child); } else { RemoveWhitespaceTextNodes(child); } } } RemoveWhitespaceTextNodes(doc1.DocumentNode); RemoveWhitespaceTextNodes(doc2.DocumentNode);
步骤4:递归比较DOM节点
写一个递归函数,逐节点比较类型、标签名、属性、子节点和文本内容:
private static bool AreNodesEquivalent(HtmlNode node1, HtmlNode node2) { // 节点类型不同直接不等 if (node1.NodeType != node2.NodeType) return false; // 处理元素节点 if (node1.NodeType == HtmlNodeType.Element) { // 标签名不区分大小写 if (!string.Equals(node1.Name, node2.Name, StringComparison.OrdinalIgnoreCase)) return false; // 比较属性:数量一致,且每个属性的键(不区分大小写)和值都相同 if (node1.Attributes.Count != node2.Attributes.Count) return false; foreach (var attr in node1.Attributes) { var matchingAttr = node2.Attributes[attr.Name]; if (matchingAttr == null || !string.Equals(attr.Value, matchingAttr.Value, StringComparison.Ordinal)) return false; } } // 处理文本节点:忽略前后空白 else if (node1.NodeType == HtmlNodeType.Text) { return string.Equals(node1.InnerText.Trim(), node2.InnerText.Trim(), StringComparison.Ordinal); } // 子节点数量不一致直接不等 if (node1.ChildNodes.Count != node2.ChildNodes.Count) return false; // 递归比较每个子节点 for (int i = 0; i < node1.ChildNodes.Count; i++) { if (!AreNodesEquivalent(node1.ChildNodes[i], node2.ChildNodes[i])) return false; } return true; } // 调用比较 bool isEquivalent = AreNodesEquivalent(doc1.DocumentNode, doc2.DocumentNode);
额外考虑
如果TinyMce还会生成一些额外的属性(比如data-mce-*类的私有属性),你可以在标准化步骤里把这些无关属性删掉,避免影响比较结果。
内容的提问来源于stack exchange,提问作者sdfsd
相关产品推荐
相关产品推荐

