HTMLAgilityPack error: "Multiple node elements can't be created."

Viewed 651

I'm attempting to use the HTMLAgilityPack to get retrieve and edit inner text of some HTML. The inner text of each node i retrieve needs to be checked for matching strings and those matching strings to be highlighted like so:

var HtmlDoc = new HtmlDocument();
HtmlDoc.LoadHtml(item.Content);

var nodes = HtmlDoc.DocumentNode.SelectNodes("//div[@class='guide_subtitle_cell']/p");
foreach (HtmlNode htmlNode in nodes)
{
    htmlNode.ParentNode.ReplaceChild(HtmlTextNode.CreateNode(Methods.HighlightWords(htmlNode.InnerText, searchstring)), htmlNode);
}

This is the code for the HighlightWords method I use:

public static string HighlightWords(string input, string searchstring)
    {
        if (input == null || searchstring == null)
        {
            return input;
        }

        var lowerstring = searchstring.ToLower();
        var words = lowerstring.Split(' ').ToList();

        for (var i = 0; i < words.Count; i++)
        {
            Match m = Regex.Match(input, words[i], RegexOptions.IgnoreCase);

            if (m.Success)
            {
                string ReplaceWord = string.Format("<span class='search_highlight'>{0}</span>", m.Value);
                input = Regex.Replace(input, words[i], ReplaceWord, RegexOptions.IgnoreCase);
            }
        }

        return input;
}

Can anyone suggest how to get this working or indicate what i'm doing wrong?

2 Answers

The problem is that HtmlTextNode.CreateNode can only create one node. When you add a <span> inside, that's another node, and CreateNode throws the exception you see.

Make sure that you are only doing a search and replace on the lowest leaf nodes (nodes with no children). Then rebuild that node by:

  1. Create a new empty node to replace the old one
  2. Search for the text in .InnerText
  3. Use HtmlTextNode.Create to add the plain text before the text you want to highlight
  4. Then add your new <span> with the highlighted text with HtmlNode.CreateNode
  5. Then search for the next occurrence (start back at 1) until no more occurrences are found.

Your function HighlightWords must be returning multiple top-level HTML nodes. For example:

<p>foo</p>
<span>bar</span>

The HtmlAgilityPack only allows one top-level node to be returned. You can hardcode the return value for HighlightWords to test.

Also, this post has run across the same problem.

Related