LoginSignup
2
2

More than 5 years have passed since last update.

c#のラムダ式などを使って閉じ忘れのないhtmlビルダーを作りたい

Last updated at Posted at 2015-05-18

・C#で構造的にhtmlを作成したい。
・htmlの閉じ忘れが無いようにしたい。
・C#でhtmlの構造(入れ子)が表現できるようにしたい。
・StringBuilderを使って高速に作成したい。
みたいな事を徒然と考えながらコーディング。いつも思うけど、きっともっといい方法があるはずや!

NuGetで以下してます。
PM> Install-Package IX-MAIN

Program.cs
using System;
using System.Linq;
namespace Implem.Sample
{
    class Program
    {
        static void Main(string[] args)
        {
            // 使用例1
            var htmlBuilder1 = new HtmlBuilder();
            Console.WriteLine(
                htmlBuilder1.Append("div", () =>
                    htmlBuilder1.Append("p", "content", () =>
                        htmlBuilder1.AppendText("hoge"))));

            // 使用例2
            var htmlBuilder2 = new HtmlBuilder();
            Console.WriteLine(
                htmlBuilder2.Append("div", () =>
                    htmlBuilder2.Append("a", new string[]
                    {
                        "id", "hoge",
                        "class", "fuga",
                        "href", "http://qiita.com/"
                    },
                    () =>
                        htmlBuilder2.AppendText("foo"))));

            // 使用例3
            var htmlBuilder3 = new HtmlBuilder();
            Console.WriteLine(
                htmlBuilder3.Append("table", () =>
                    Enumerable.Range(1, 2).ForEach(x =>
                        htmlBuilder3.Append("tr", () =>
                            Enumerable.Range(1, 2).ForEach(y =>
                            {
                                if (x == 1)
                                {
                                    htmlBuilder3.Append("th", () =>
                                        htmlBuilder3.AppendText(x + "-" + y));
                                }
                                else
                                {
                                    htmlBuilder3.Append("td", () =>
                                        htmlBuilder3.AppendText(x + "-" + y));
                                }
                            })))));
        }
    }
}
output.html
<div><p class="content">hoge</p></div>
<div><a id="hoge" class="fuga" href="http://qiita.com/">foo</a></div>
<table><tr><th>1-1</th><th>1-2</th></tr><tr><td>2-1</td><td>2-2</td></tr></table>
HtmlBuilder.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Implem.Sample
{
    public class HtmlBuilder
    {
        private StringBuilder html = new StringBuilder();
        private List<string> closeTagCollection = new List<string>();

        private enum AttributePropertyTypes : int
        {
            Name = 0,
            Value = 1
        }

        // タグ追加
        public HtmlBuilder Append(string tag, int closeLevel = 0, params string[] attributes)
        {
            html.Append("<", tag);
            attributes
                .Select((o, i) => new { Value = o, PropertyType = (AttributePropertyTypes)(i % 2) })
                .ForEach(data =>
                {
                    switch (data.PropertyType)
                    {
                        case AttributePropertyTypes.Name:

                            // 偶数はプロパティ名
                            html.Append(" " + data.Value); break;

                        case AttributePropertyTypes.Value:

                            // 奇数は値
                            html.Append("=\"", data.Value, "\"");break;
                    }
                });
            if (closeLevel > 0)
            {
                html.Append(" />");
                if (closeLevel >= 2)
                {
                    AppendCloseTagCollection(closeLevel - 1);
                }
            }
            else
            {
                html.Append(">");
                closeTagCollection.Insert(0, "</" + tag + ">");
            }
            return this;
        }

        // 属性付きタグ追加の間に別の処理
        public HtmlBuilder Append(string tag, string[] attributes, Action action)
        {
            Append(tag, attributes: attributes);
            action();
            AppendClose();
            return this;
        }

        // タグ追加の間に別の処理
        public HtmlBuilder Append(string tag, Action action)
        {
            Append(tag, attributes: new string[] { });
            action();
            AppendClose();
            return this;
        }

        // id, class属性付きタグ追加の間に別の処理
        public HtmlBuilder Append(string tag, string controlId, string cssClass, Action action)
        {
            Append(tag, attributes: new string[] { "id", controlId, "class", cssClass });
            action();
            AppendClose();
            return this;
        }

        // class属性付きタグ追加の間に別の処理
        public HtmlBuilder Append(string tag, string cssClass, Action action)
        {
            Append(tag, attributes: new string[] { "class", cssClass });
            action();
            AppendClose();
            return this;
        }

        // テキスト追加とクローズ
        public HtmlBuilder AppendText(string str, int closeLevel = 0)
        {
            html.Append(str);
            AppendCloseTagCollection(closeLevel);
            return this;
        }

        // クローズ
        public HtmlBuilder AppendClose(int closeLevel = 1)
        {
            AppendCloseTagCollection(closeLevel);
            return this;
        }

        // 全部クローズ
        public HtmlBuilder AppendCloseAll()
        {
            AppendCloseTagCollection(-1);
            return this;
        }

        // 全部クローズしてHtml出力
        public override string ToString()
        {
            // html出力前に閉じ忘れしないよう全部クローズ
            AppendCloseTagCollection();
            return html.ToString();
        }

        // クローズ処理
        private void AppendCloseTagCollection(int closeLevel = -1)
        {
            if (closeTagCollection.Count() > 0)
            {
                if (closeLevel == -1)
                {
                    // -1 : 全部閉じる
                    html.Append(string.Join("", closeTagCollection));
                    closeTagCollection.Clear();
                }
                else
                {
                    // n : n個閉じる
                    html.Append(string.Join(string.Empty, closeTagCollection.Take(closeLevel)));
                    closeTagCollection = closeTagCollection.Skip(closeLevel).ToList<string>();
                }
            }
        }
    }
}
Extensions.cs
using System.Linq;
using System.Text;
namespace Implem.Sample
{
    public static class Extensions
    {
        // 連続Append用拡張メソッド
        public static void Append(this StringBuilder stringBuilder, params string[] strings)
        {
            strings.ForEach(str => stringBuilder.Append(str));
        }
    }
}

ソースコード自動生成への道は長くて険しい。

2
2
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2