Better way to add a style attribute to Html using HtmlAgilityPack

c# html html-agility-pack

Question

I am using the HtmlAgilityPack. I am searching through all P tags and adding a "margin-top: 0px" to the style within the P tag.

As you can see it is kinda "brute forcing" the margin-top attribute. It seems there has to be a better way to do this using the HtmlAgilityPack but I could not find it, and the HtmlAgilityPack documentation is non-existent.

Anybody know a better way?

        HtmlNodeCollection pTagNodes = node.SelectNodes("//p[not(contains(@style,'margin-top'))]");
        if (pTagNodes != null && pTagNodes.Any())
        {
            foreach (HtmlNode pTagNode in pTagNodes)
            {
                if (pTagNode.Attributes.Contains("style"))
                {
                    string styles = pTagNode.Attributes["style"].Value;
                    pTagNode.SetAttributeValue("style", styles + "; margin-top: 0px");
                }
                else
                {
                    pTagNode.Attributes.Add("style", "margin-top: 0px");
                }
            }
        }



UPDATE: I have modified the code based on Alex's suggestions. Would still like to know if there is a some built-in functionality in HtmlAgilityPack that will handle the style attributes in a more "DOM" manner.

        const string margin = "; margin-top: 0px";

        HtmlNodeCollection pTagNodes = node.SelectNodes("//p[not(contains(@style,'margin-top'))]");
        if (pTagNodes != null && pTagNodes.Any())
        {
            foreach (var pTagNode in pTagNodes)
            {
                string styles = pTagNode.GetAttributeValue("style", "");
                pTagNode.SetAttributeValue("style", styles + margin);
            }
        }

Accepted Answer

You could simplify your code a little bit by using HtmlNode.GetAttributeValue method, and making your "margin-top" magic string as constant:

const string margin = "margin-top: 0";
foreach (var pTagNode in pTagNodes)
{
    var styles = pTagNode.GetAttributeValue("style", null);
    var separator = (styles == null ? null : "; ");
    pTagNode.SetAttributeValue("style", styles + separator + margin);
}

Not a very significant improvement, but this code is simpler as for me.


Popular Answer

First of all, are you sure you need more than what you asked for? Alex solution should just work fine for your current problem, if it's always that "simple" why bother and add more complexity to it?

Anway, the AgilityPack doesn't have that kind of function, but surely the .Net Framework has. Note this is all for .Net 4, if you're using an earlier version things might be a bit different. First of, System.Web.dll comes with the CssStyleCollection Class, this class already has everything build in that you could want for parsing inline css, there's just one catch, it's constructor is internal so the solution is a bit "hacky". First off, for construction an instance of the class all you need is a bit of reflection, the code for that has already been done here. Just keep in mind that this works now, but could break in a future version of .Net. All that's left is really easy

CssStyleCollection css = CssStyleTools.Create();
css.Value = "border-top:1px dotted #BBB;margin-top: 0px;font-size:12px";
Console.WriteLine(css["margin-top"]); //prints "0px"

IF you can't for some reason add a reference to System.Web (would be the case if you're using .Net 4 Client Profile) there's always the possibility to use Reflector.

Personally i'd go with Alex's solution, but it's up to you to decide. :)



Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow