Thursday, February 23, 2012

Simple fluent and recursive tag builder

The goal was to write a simple fluent and recursive tag builder.

Simple – means the implementation should be as short as possible.

Fluent – means a fluent interface

Recursive – means that tags could possibly be nested one inside the other.

The implementation is as follows:

public class TagBuilder
{
    public TagBuilder() { }
 
    public TagBuilder( string TagName, TagBuilder Parent )
    {
        this.tagName = TagName;
        this.parent = Parent;
    }
 
    private string        tagName;
    private TagBuilder    parent;
    private StringBuilder body = new StringBuilder();
    private Dictionary<string, string> _attributes = new Dictionary<string, string>();
 
    public TagBuilder AddContent( string Content )
    {
        body.Append( Content );
        return this;
    }
 
    public TagBuilder AddContentFormat( string Format, params object[] args )
    {
        body.AppendFormat( Format, args );
        return this;
    }
 
    public TagBuilder StartTag( string TagName )
    {
        TagBuilder tag = new TagBuilder( TagName, this );
 
        return tag;
    }
 
    public TagBuilder EndTag()
    {
        parent.AddContent( this.ToString() );
        return parent;
    }
 
    public TagBuilder AddAttribute( string Name, string Value )
    {
        _attributes.Add( Name, Value );
        return this;
    }
 
    public override string ToString()
    {
        StringBuilder tag = new StringBuilder();
 
        // preamble
        if ( !string.IsNullOrEmpty( this.tagName ) )
            tag.AppendFormat( "<{0}", tagName );
 
        if ( _attributes.Count > 0 )
        {
            tag.Append( " " );
            tag.Append( 
                string.Join( " ", 
                    _attributes
                        .Select( 
                            kvp => string.Format( "{0}='{1}'", kvp.Key, kvp.Value ) )
                        .ToArray() ) );
        }
 
        // body/ending
        if ( body.Length > 0 )
        {
            if ( !string.IsNullOrEmpty( this.tagName) || this._attributes.Count > 0 )
                tag.Append( ">" );
            tag.Append( body.ToString() );
            if ( !string.IsNullOrEmpty( this.tagName ) )
                tag.AppendFormat( "</{0}>", this.tagName );
        }
        else
            if ( !string.IsNullOrEmpty( this.tagName ) )
                tag.Append( "/>" );
 
        return tag.ToString();
    }
}

Example usage:

var script = 
 tag.StartTag( "parent" )
     .AddAttribute( "parentproperty1", "true" )
     .AddAttribute( "parentproperty2", "5" )
         .StartTag( "child1")
         .AddAttribute( "childproperty1", "c" )
         .AddContent( "childbody" )
         .EndTag()
         .StartTag( "child2" )
         .AddAttribute( "childproperty2", "c" )
         .AddContent( "childbody" )
         .EndTag()
     .EndTag()
     .StartTag( "script" )
     .AddContent( "$.scriptbody();")
     .EndTag()
     .ToString();

Note that I can nest the hierarchy plus I can have two or more tags one beside the other (siblings). The output of this example call is:

<parent parentproperty1='true' parentproperty2='5'><child1 childproperty1='c'>ch
ildbody</child1><child2 childproperty2='c'>childbody</child2></parent><script>$.
scriptbody();</script>

Feel free to modify the code. An example extension would be to implement indentation.

4 comments:

londonwebsite said...
This comment has been removed by a blog administrator.
liuhongbo said...

how can you produce the following data with the code:

<a>x<b>y</b>x</a>

Wiktor Zychla said...

@liuhongbo: sure you can:

var _example =

tag.StartTag( "a" )

.AddContent( "x" )

.StartTag( "b" )

.AddContent( "y" )

.EndTag()

.AddContent( "x" )

.EndTag()

.ToString();

nameless said...

Great class. Very intuitive way of building HTML on the server side. Thanks for sharing