Meilleure façon de combiner des nœuds avec le pack d'agilité HTML

c# html-agility-pack

Question

J'ai converti un gros document de Word en HTML. C'est proche, mais j'ai un tas de nœuds "code" que j'aimerais fusionner en un nœud "pré".

Voici l'entrée:

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

Je veux le transformer en ceci, à la place:

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

J'ai fini par écrire une boucle de force brute qui itère les nœuds à la recherche de nœuds consécutifs, mais cela me semble moche:

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

Normalement, je supprime les noeuds dans la première boucle, mais cela ne fonctionne pas pendant l'itération, car vous ne pouvez pas modifier la collection lorsque vous le parcourez.

De meilleures idées?

Réponse populaire

La première approche: sélectionnez tous les nœuds <code> , regroupez-les et créez un nœud <pre> par groupe:

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

Le champ de regroupement ici est un champ combiné d'un nœud parent et d'un index de groupe qui est augmenté lorsqu'un nouveau groupe est trouvé. Aussi, j'ai utilisé la méthode d'extension NextSiblingIsCode ici:

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

Il permettait de déterminer si le frère suivant était un nœud <code> .


La deuxième approche: sélectionnez uniquement le nœud <code> de chaque groupe, puis parcourez chacun de ces nœuds pour rechercher le nœud <code> jusqu'au premier nœud non <code> . J'ai utilisé xpath ici:

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



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi