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

发布于 2024-09-12 21:56:00 字数 1607 浏览 3 评论 0原文

我已将大型文档从 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();
}

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

更好的想法?

I've converted a large document from Word to HTML. It's close, but I have a bunch of "code" nodes that I'd like to merge into one "pre" node.

Here's the input:

<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>

I want to turn it into this, instead:

<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>

I ended up writing a brute-force loop that iterates nodes looking for consecutive ones, but this seems ugly to me:

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();
}

Normally I'd remove the nodes in the first loop, but that doesn't work during iteration since you can't change the collection as you iterate over it.

Better ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

超可爱的懒熊 2024-09-19 21:56:00

第一种方法:选择所有节点,将它们分组,并为每个组创建一个

节点:

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();
}

这里的分组字段是a的组合字段父节点和组索引,当发现新组时增加。
我还在这里使用了 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");
}

它用于确定下一个同级是否是 节点。


The second approach: select only the top <code> node of each group, then iterate through each of these nodes to find the next <code> node until the first non-<code> node. I used xpath here:

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())
    ));
}

The first approach: select all the <code> nodes, group them, and create a <pre> node per group:

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();
}

The grouping field here is combined field of a parent node and group index which is increased when new group is found.
Also I used NextSiblingIsCode extension method here:

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");
}

It used to determine whether the next sibling is a <code> node.


The second approach: select only the top <code> node of each group, then iterate through each of these nodes to find the next <code> node until the first non-<code> node. I used xpath here:

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())
    ));
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文