Generate HTML table from list of generic class wit

2020-02-09 10:34发布

I want to generate an HTML table from a couple specified parameters. Specifically, the two parameters I want to pass into my method are: IEnumerable list, and some subset of properties of T. For example, let's say I have a List of this class:

class Person
{
  string FirstName
  string MiddleName
  string LastName
}

Let's say the list has 5 people in it. I want to be able to get an HTML table of that class (or any other arbitrary class) by doing something like this:

List<Person> people;
...add people to list

string HTML = GetMyTable(people, "FirstName", "LastName");

I'm sure there's a better way to specify which properties I want the table generated from (or which properties I want excluded from the table, that would be better since I'll usually want most or all of the class's properties), but I'm not sure how (I've never used reflection, but I'm guessing that's how). Also, the method should accept a list of any type of class.

Any clever ideas on how to accomplish this?

4条回答
混吃等死
2楼-- · 2020-02-09 11:04

Here are two approaches, one using reflection:

public static string GetMyTable(IEnumerable list, params string[] columns)
{
    var sb = new StringBuilder();
    foreach (var item in list)
    {
        //todo this should actually make an HTML table, not just get the properties requested
        foreach (var column in columns)
            sb.Append(item.GetType().GetProperty(column).GetValue(item, null));
    }
    return sb.ToString();
}
//used like
string HTML = GetMyTable(people, "FirstName", "LastName");

Or using lambdas:

public static string GetMyTable<T>(IEnumerable<T> list, params Func<T, object>[] columns)
{
    var sb = new StringBuilder();
    foreach (var item in list)
    {
        //todo this should actually make an HTML table, not just get the properties requested
        foreach (var column in columns)
            sb.Append(column(item));
    }
    return sb.ToString();
}
//used like
string HTML = GetMyTable(people, x => x.FirstName, x => x.LastName);

With the lambdas, what's happening is you're passing methods to the GetMyTable method to get each property. This has benefits over reflection like strong typing, and probably performance.

查看更多
神经病院院长
3楼-- · 2020-02-09 11:05

This is what I did and it seems to work fine and not a huge performance hit.

    public static string ToHtmlTable<T>(this List<T> listOfClassObjects)
    {
        var ret = string.Empty;

        return listOfClassObjects == null || !listOfClassObjects.Any()
            ? ret
            : "<table>" +
              listOfClassObjects.First().GetType().GetProperties().Select(p => p.Name).ToList().ToColumnHeaders() +
              listOfClassObjects.Aggregate(ret, (current, t) => current + t.ToHtmlTableRow()) +
              "</table>";
    }

    public static string ToColumnHeaders<T>(this List<T> listOfProperties)
    {
        var ret = string.Empty;

        return listOfProperties == null || !listOfProperties.Any()
            ? ret
            : "<tr>" +
              listOfProperties.Aggregate(ret,
                  (current, propValue) =>
                      current +
                      ("<th style='font-size: 11pt; font-weight: bold; border: 1pt solid black'>" +
                       (Convert.ToString(propValue).Length <= 100
                           ? Convert.ToString(propValue)
                           : Convert.ToString(propValue).Substring(0, 100)) + "..." + "</th>")) +
              "</tr>";
    }

    public static string ToHtmlTableRow<T>(this T classObject)
    {
        var ret = string.Empty;

        return classObject == null
            ? ret
            : "<tr>" +
              classObject.GetType()
                  .GetProperties()
                  .Aggregate(ret,
                      (current, prop) =>
                          current + ("<td style='font-size: 11pt; font-weight: normal; border: 1pt solid black'>" +
                                     (Convert.ToString(prop.GetValue(classObject, null)).Length <= 100
                                         ? Convert.ToString(prop.GetValue(classObject, null))
                                         : Convert.ToString(prop.GetValue(classObject, null)).Substring(0, 100) +
                                           "...") +
                                     "</td>")) + "</tr>";
    }

To use it just pass the ToHtmlTable() a List Example:

List documents = GetMyListOfDocuments(); var table = documents.ToHtmlTable();

查看更多
Fickle 薄情
4楼-- · 2020-02-09 11:11

Maybe something like this?

var html = GetMyTable(people, x => x.LastName, x => x.FirstName);

public static string GetMyTable<T>(IEnumerable<T> list,params Func<T,object>[] fxns)
{

    StringBuilder sb = new StringBuilder();
    sb.Append("<TABLE>\n");
    foreach (var item in list)
    {
        sb.Append("<TR>\n");
        foreach(var fxn in fxns)
        {
            sb.Append("<TD>");
            sb.Append(fxn(item));
            sb.Append("</TD>");
        }
        sb.Append("</TR>\n");
    }
    sb.Append("</TABLE>");

    return sb.ToString();
}

--Version 2.0--

public static string GetMyTable<T>(IEnumerable<T> list, params  Expression<Func<T, object>>[] fxns)
{

    StringBuilder sb = new StringBuilder();
    sb.Append("<TABLE>\n");

    sb.Append("<TR>\n");
    foreach (var fxn in fxns)
    {
        sb.Append("<TD>");
        sb.Append(GetName(fxn));
        sb.Append("</TD>");
    }
    sb.Append("</TR> <!-- HEADER -->\n");


    foreach (var item in list)
    {
        sb.Append("<TR>\n");
        foreach (var fxn in fxns)
        {
            sb.Append("<TD>");
            sb.Append(fxn.Compile()(item));
            sb.Append("</TD>");
        }
        sb.Append("</TR>\n");
    }
    sb.Append("</TABLE>");

    return sb.ToString();
}

static string GetName<T>(Expression<Func<T, object>> expr)
{
    var member = expr.Body as MemberExpression;
    if (member != null)
        return GetName2(member);

    var unary = expr.Body as UnaryExpression;
    if (unary != null)
        return GetName2((MemberExpression)unary.Operand);

    return "?+?";
}

static string GetName2(MemberExpression member)
{
    var fieldInfo = member.Member as FieldInfo;
    if (fieldInfo != null)
    {
        var d = fieldInfo.GetCustomAttribute(typeof(DescriptionAttribute)) as DescriptionAttribute;
        if (d != null) return d.Description;
        return fieldInfo.Name;
    }

    var propertInfo = member.Member as PropertyInfo;
    if (propertInfo != null)
    {
        var d = propertInfo.GetCustomAttribute(typeof(DescriptionAttribute)) as DescriptionAttribute;
        if (d != null) return d.Description;
        return propertInfo.Name;
    }

    return "?-?";
}

PS: Calling fxn.Compile() repeatedly can be performance killer in a tight loop. It can be better to cache it in a dictionary .

查看更多
smile是对你的礼貌
5楼-- · 2020-02-09 11:22

Good luck with

Extention Method

  public static class EnumerableExtension
{
    public static string ToHtmlTable<T>(this IEnumerable<T> list, List<string> headerList, List<CustomTableStyle> customTableStyles, params Func<T, object>[] columns)
    {
        if (customTableStyles == null)
            customTableStyles = new List<CustomTableStyle>();

        var tableCss = string.Join(" ", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Table).Where(w => w.ClassNameList != null).SelectMany(s => s.ClassNameList)) ?? "";
        var trCss = string.Join(" ", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Tr).Where(w => w.ClassNameList != null).SelectMany(s => s.ClassNameList)) ?? "";
        var thCss = string.Join(" ", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Th).Where(w => w.ClassNameList != null).SelectMany(s => s.ClassNameList)) ?? "";
        var tdCss = string.Join(" ", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Td).Where(w => w.ClassNameList != null).SelectMany(s => s.ClassNameList)) ?? "";

        var tableInlineCss = string.Join(";", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Table).Where(w => w.InlineStyleValueList != null).SelectMany(s => s.InlineStyleValueList?.Select(x => String.Format("{0}:{1}", x.Key, x.Value)))) ?? "";
        var trInlineCss = string.Join(";", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Tr).Where(w => w.InlineStyleValueList != null).SelectMany(s => s.InlineStyleValueList?.Select(x => String.Format("{0}:{1}", x.Key, x.Value)))) ?? "";
        var thInlineCss = string.Join(";", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Th).Where(w => w.InlineStyleValueList != null).SelectMany(s => s.InlineStyleValueList?.Select(x => String.Format("{0}:{1}", x.Key, x.Value)))) ?? "";
        var tdInlineCss = string.Join(";", customTableStyles?.Where(w => w.CustomTableStylePosition == CustomTableStylePosition.Td).Where(w => w.InlineStyleValueList != null).SelectMany(s => s.InlineStyleValueList?.Select(x => String.Format("{0}:{1}", x.Key, x.Value)))) ?? "";

        var sb = new StringBuilder();

        sb.Append($"<table{(string.IsNullOrEmpty(tableCss) ? "" : $" class=\"{tableCss}\"")}{(string.IsNullOrEmpty(tableInlineCss) ? "" : $" style=\"{tableInlineCss}\"")}>");
        if (headerList != null)
        {
            sb.Append($"<tr{(string.IsNullOrEmpty(trCss) ? "" : $" class=\"{trCss}\"")}{(string.IsNullOrEmpty(trInlineCss) ? "" : $" style=\"{trInlineCss}\"")}>");
            foreach (var header in headerList)
            {
                sb.Append($"<th{(string.IsNullOrEmpty(thCss) ? "" : $" class=\"{thCss}\"")}{(string.IsNullOrEmpty(thInlineCss) ? "" : $" style=\"{thInlineCss}\"")}>{header}</th>");
            }
            sb.Append("</tr>");
        }
        foreach (var item in list)
        {
            sb.Append($"<tr{(string.IsNullOrEmpty(trCss) ? "" : $" class=\"{trCss}\"")}{(string.IsNullOrEmpty(trInlineCss) ? "" : $" style=\"{trInlineCss}\"")}>");
            foreach (var column in columns)
                sb.Append($"<td{(string.IsNullOrEmpty(tdCss) ? "" : $" class=\"{tdCss}\"")}{(string.IsNullOrEmpty(tdInlineCss) ? "" : $" style=\"{tdInlineCss}\"")}>{column(item)}</td>");
            sb.Append("</tr>");
        }

        sb.Append("</table>");

        return sb.ToString();
    }

    public class CustomTableStyle
    {
        public CustomTableStylePosition CustomTableStylePosition { get; set; }

        public List<string> ClassNameList { get; set; }
        public Dictionary<string, string> InlineStyleValueList { get; set; }
    }

    public enum CustomTableStylePosition
    {
        Table,
        Tr,
        Th,
        Td
    }
}

Using

 private static void Main(string[] args)
    {
        var dataList = new List<TestDataClass>
        {
            new TestDataClass {Name = "A", Lastname = "B", Other = "ABO"},
            new TestDataClass {Name = "C", Lastname = "D", Other = "CDO"},
            new TestDataClass {Name = "E", Lastname = "F", Other = "EFO"},
            new TestDataClass {Name = "G", Lastname = "H", Other = "GHO"}
        };

        var headerList = new List<string> { "Name", "Surname", "Merge" };

        var customTableStyle = new List<EnumerableExtension.CustomTableStyle>
        {
            new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Table, InlineStyleValueList = new Dictionary<string, string>{{"font-family", "Comic Sans MS" },{"font-size","15px"}}},
            new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Table, InlineStyleValueList = new Dictionary<string, string>{{"background-color", "yellow" }}},
            new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Tr, InlineStyleValueList =new Dictionary<string, string>{{"color","Blue"},{"font-size","10px"}}},
            new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Th,ClassNameList = new List<string>{"normal","underline"}},
            new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Th,InlineStyleValueList =new Dictionary<string, string>{{ "background-color", "gray"}}},
            new EnumerableExtension.CustomTableStyle{CustomTableStylePosition = EnumerableExtension.CustomTableStylePosition.Td, InlineStyleValueList  =new Dictionary<string, string>{{"color","Red"},{"font-size","15px"}}},
        };

        var htmlResult = dataList.ToHtmlTable(headerList, customTableStyle, x => x.Name, x => x.Lastname, x => $"{x.Name} {x.Lastname}");
    }

    private class TestDataClass
    {
        public string Name { get; set; }
        public string Lastname { get; set; }
        public string Other { get; set; }
    }

Result

<table class="normal underline" style="font-family:Comic Sans MS;font-size:15px;background-color:yellow">
<tr style="color:Blue;font-size:10px">
    <th style="background-color:gray">Name</th>
    <th style="background-color:gray">Surname</th>
    <th style="background-color:gray">Merge</th>
</tr>
<tr style="color:Blue;font-size:10px">
    <td style="color:Red;font-size:15px">A</td>
    <td style="color:Red;font-size:15px">B</td>
    <td style="color:Red;font-size:15px">A B</td>
</tr>
<tr style="color:Blue;font-size:10px">
    <td style="color:Red;font-size:15px">C</td>
    <td style="color:Red;font-size:15px">D</td>
    <td style="color:Red;font-size:15px">C D</td>
</tr>
<tr style="color:Blue;font-size:10px">
    <td style="color:Red;font-size:15px">E</td>
    <td style="color:Red;font-size:15px">F</td>
    <td style="color:Red;font-size:15px">E F</td>
</tr>
<tr style="color:Blue;font-size:10px">
    <td style="color:Red;font-size:15px">G</td>
    <td style="color:Red;font-size:15px">H</td>
    <td style="color:Red;font-size:15px">G H</td
</tr>

查看更多
登录 后发表回答