LINQ: Expressive Html Tag Building

I hate building HTML tags in code, but it needs to be done. I just wanted to make it a little cleaner. So I came up with this method that utilizes LINQ expressions to generate the attributes for the tag. This is really only clean with simple tags, but I use it with my other tag building methods to keep them clean as well. I've seen others look for something like this and thought it'd be helpful posting it here. I haven't deeply tested this code but it shows the general concept and I'm sure it needs cleaned up a little. The following is an example calling the Tag method:

   1:  Tag( "a", "The Technical Adventures of Adam Weigert", href => "http://weblogs.asp.net/adweigert/" );
   2:  Tag( "div", "LINQ Expressions Rock", style => "font-size: 250%; font-weight: bold;", id => "title" );

This is the actual method, I love how simple the LINQ expression makes building the attributes.

   1:  public string Tag( string tagName, string innerHtml, params Expression<Func<string, string>>[] attributes )
   2:  {
   3:      XElement tag = new XElement( XName.Get( tagName, string.Empty ) );
   4:      
   5:      if ( attributes != null )
   6:      {
   7:          foreach ( var attribute in attributes )
   8:          {
   9:              string attributeName = attribute.Parameters[ 0 ].Name;
  10:              string attributeValue = attribute.Compile()(string.Empty);
  11:   
  12:              tag.SetAttributeValue( XName.Get( attributeName, string.Empty ), attributeValue );
  13:          }
  14:      }
  15:   
  16:      if ( !string.IsNullOrEmpty( innerHtml ) )
  17:      {
  18:          tag.Add( XElement.Parse( "<xml>" + innerHtml + "</xml>" ).Nodes() );
  19:      }
  20:   
  21:      return tag.ToString();
  22:  }

Update: Used XElement instead, and better innerHtml handling. Thanks to everyone that helped improve this method.

14 Comments

  • Why not just build as XElement(s) and call ToString?

  • I have to agree. I think using XElement/XDocument would be the way to go as it also gives you many XML-related methods/properties to help you out forming valid XHTML/XML/HTML.

  • You could optionally replace the foreach loop with a Linq query ...

    if (attributes != null)
    {
    tag.Append(" " +
    string.Join(" ",
    (from a in attributes
    select new
    {
    value =
    string.Format("{0}=\"{1}\"",
    a.Parameters[0].Name,
    HttpUtility.HtmlEncode(a.Compile()(string.Empty)))
    }).Select(v => v.value).ToArray()));
    }

  • I updated it to use XElement instead.

  • With the new XElement version, an XmlException is thrown every time in the Try block.

    "Data at the root level is invalid. Line 1, position 1."

    Having an exception occur everytime is very expensive. You'd be better off just using tag.Add(innerHtml) or even tag.Value = innerHtml.

  • I guess I could change the param to innerText instead, because that's what tag.Add(innerHtml) or tag.Value = innerHtml would do (it doesn't parse the value). There is no InnerXml property I could set as far as I know.

  • Tag("div", "Hello World") will render text as "Hello World". Like I said, simply changing the parameter name to text or innerText would allow me to always just use tag.Value = text and only use this for simple tags.

  • Are you sure? For Tag("div", "Hello World"), I get the following value:

    "&lt;h1&gt;Hello World&lt;/h1&gt;"

    Maybe you're looking at the value that is rendered by your browser? The browser is of course going to decode any encoded Html characters ... which is expected behavior.

  • Taking a second look, I guess what you mean is you may want an actual tag rendered within a . I think the line of code below will handle innerHtml regardless if innerHtml is "some arbitrary text" or "Hello World". You can see the line below just wraps innerHtml into tags so Parse() won't fail.

    tag.Add(XElement.Parse("" + innerHtml + "").FirstNode);

    Having an innerText parameter (in addition to innerHtml) might be handy depending on if you want the literal text Hello World to display or Hello World in an tag.

  • A more robust line of code would be:

    tag.Add(XElement.Parse("" + innerHtml + "").Nodes());

    This will also handle an innerHtml value like the one below. The line of code in my last comment using FirstNode would only return the first tag.

    Hello WorldAnother h1

  • Thanks guys, I've updated the code.

  • im getting
    CS0246: The type or namespace name 'var' could not be found (are you missing a using directive or an assembly reference?)

    eror at

    foreach ( var attribute in attributes )


    any idea why?

  • I would imagine you aren't using the .NET 3+ compiler.

  • i am using .net 3.5

Comments have been disabled for this content.