将节点与Html Agility Pack结合的最佳方法

时间:2010-08-03 22:35:34

标签: c# html-agility-pack

我已将大型文档从Word转换为HTML。它很接近,但我有一堆“代码”节点,我想合并到一个“预”节点。

这是输入:

<p>Here's a sample MVC Controller action:</p>
<code>        public ActionResult Index()</code>
<code>        {</code>
<code>            return View();</code>
<code>        }</code>
<p>We'll start by making the following changes...</p>

我想把它变成这个,而不是:

<p>Here's a sample MVC Controller action:</p>
<pre class="brush: csharp">        public ActionResult Index()
    {
        return View();
    }</pre>
<p>We'll start by making the following changes...</p>

我最后编写了一个暴力循环来迭代寻找连续节点的节点,但这对我来说似乎很难看:

HtmlDocument doc = new HtmlDocument();
doc.Load(file);

var nodes = doc.DocumentNode.ChildNodes;
string contents = string.Empty;

foreach (HtmlNode node in nodes)
{

    if (node.Name == "code")
    {
        contents += node.InnerText + Environment.NewLine;
        if (node.NextSibling.Name != "code" && 
            !(node.NextSibling.Name == "#text" && node.NextSibling.NextSibling.Name == "code")
            )
        {
            node.Name = "pre";
            node.Attributes.RemoveAll();
            node.SetAttributeValue("class", "brush: csharp");
            node.InnerHtml = contents;
            contents = string.Empty;
        }
    }
}

nodes = doc.DocumentNode.SelectNodes(@"//code");
foreach (var node in nodes)
{
    node.Remove();
}

通常我会删除第一个循环中的节点,但这在迭代期间不起作用,因为在迭代时不能更改集合。

更好的想法?

2 个答案:

答案 0 :(得分:2)

第一种方法:选择所有<code>个节点,对它们进行分组,并为每个组创建一个<pre>节点:

var idx = 0;
var nodes = doc.DocumentNode
    .SelectNodes("//code")
    .GroupBy(n => new { 
        Parent = n.ParentNode, 
        Index = n.NextSiblingIsCode() ? idx : idx++ 
    });

foreach (var group in nodes)
{
    var pre = HtmlNode.CreateNode("<pre class='brush: csharp'></pre>");
    pre.AppendChild(doc.CreateTextNode(
        string.Join(Environment.NewLine, group.Select(g => g.InnerText))
    ));
    group.Key.Parent.InsertBefore(pre, group.First());

    foreach (var code in group)
        code.Remove();
}

此处的分组字段是父节点和组索引的组合字段,当找到新组时,该字段会增加。 我在这里也使用了NextSiblingIsCode扩展方法:

public static bool NextSiblingIsCode(this HtmlNode node)
{
    return (node.NextSibling != null && node.NextSibling.Name == "code") ||
        (node.NextSibling is HtmlTextNode && 
         node.NextSibling.NextSibling != null && 
         node.NextSibling.NextSibling.Name == "code");
}

它用于确定下一个兄弟是否是<code>节点。

<小时/> 第二种方法:仅选择每个组的顶部<code>节点,然后遍历每个节点以查找下一个<code>节点,直到第一个非<code>节点。我在这里使用xpath

var nodes = doc.DocumentNode.SelectNodes(
    "//code[name(preceding-sibling::*[1])!='code']"
);
foreach (var node in nodes)
{
    var pre = HtmlNode.CreateNode("<pre class='brush: csharp'></pre>");
    node.ParentNode.InsertBefore(pre, node);
    var content = string.Empty;
    var next = node;
    do
    {
        content += next.InnerText + Environment.NewLine;
        var previous = next;
        next = next.SelectSingleNode("following-sibling::*[1][name()='code']");
        previous.Remove();
    } while (next != null);
    pre.AppendChild(doc.CreateTextNode(
        content.TrimEnd(Environment.NewLine.ToCharArray())
    ));
}

答案 1 :(得分:0)