・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));
}
}
}
ソースコード自動生成への道は長くて険しい。