【问题标题】:Generate HTML table from list of generic class with specified properties从具有指定属性的通用类列表生成 HTML 表
【发布时间】:2012-06-20 18:45:45
【问题描述】:

我想从几个指定的参数生成一个 HTML 表格。具体来说,我要传递给我的方法的两个参数是:IEnumerable 列表,以及 T 的一些属性子集。例如,假设我有一个此类的列表:

class Person
{
  string FirstName
  string MiddleName
  string LastName
}

假设列表中有 5 个人。我希望能够通过执行以下操作来获取该类(或任何其他任意类)的 HTML 表:

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

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

我确信有更好的方法来指定我希望从哪些属性生成表(或者我想从表中排除哪些属性,这会更好,因为我通常需要大部分或所有类的属性) ,但我不确定如何(我从来没有使用过反射,但我猜是这样)。此外,该方法应该接受任何类型的类的列表。

关于如何做到这一点有什么聪明的想法吗?

【问题讨论】:

    标签: c# generics reflection


    【解决方案1】:

    也许是这样的?

    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();
    }
    

    --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: 反复调用fxn.Compile() 可能会在紧密循环中成为性能杀手。将其缓存在字典中会更好。

    【讨论】:

    • 例如,如果您尝试选择一个 int 和一个字符串会发生什么? P会变成object,还是不会编译?
    • 你能解释一下“fxn(item)”在做什么吗?
    • 它调用表示为 lambda(x=&gt;x.LastNamex=&gt;x.FirstName)的委托。因为x=&gt;x.LastName 等于delegate(Person x){ return x.LastName;}
    • 如果 Person 类中的每个属性都有一个 [Description] 属性(使用 ComponentModel 或 EnterpriseServices 命名空间),有没有办法在此方法中获取该文本,以便我可以使用该文本为我的表格创建标题/标题行?所以表格的第一行是“名字|姓氏”,下一行是“约翰·史密斯”,依此类推。
    • 这太好了,L.B.谢谢!
    【解决方案2】:

    这就是我所做的,它似乎工作正常,而且对性能没有太大影响。

        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>";
        }
    

    要使用它,只需将 ToHtmlTable() 传递给 List 示例:

    列出文档 = GetMyListOfDocuments(); var table = documents.ToHtmlTable();

    【讨论】:

      【解决方案3】:

      这里有两种方法,一种使用反射:

      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");
      

      或者使用 lambda:

      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);
      

      使用 lambda,您正在将方法传递给 GetMyTable 方法以获取每个属性。这比反射有好处,比如强类型,可能还有性能。

      【讨论】:

      • 感谢蒂姆的帮助。我很高兴看到有多种方法可以做到这一点。
      • lambda 方法在性能方面似乎要好得多。我的目标是导出所有属性,而不仅仅是单个属性,我如何使用 lambda 方法来实现,而不必显式列出所有属性
      【解决方案4】:

      祝你好运

      扩展方法

        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
          }
      }
      

      使用

       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; }
          }
      

      结果

      <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>
      

      【讨论】:

      • 能给个非扩展方法的版本吗?
      • 用这个public static string ToHtmlTable&lt;T&gt;(IEnumerable&lt;T&gt; list, List&lt;string&gt; headerList, List&lt;CustomTableStyle&gt; customTableStyles, params Func&lt;T, object&gt;[] columns)改变
      【解决方案5】:

      扩展@Tim 的答案

          public string GetHtmlTable<T, Tproperty>(IEnumerable<T> list, params Expression<Func<T, Tproperty>>[] columns)
          {
              var sb = new StringBuilder();
      
      
              sb.AppendLine("<table>");
              sb.AppendLine("<tr>");
              foreach (var column in columns)
              {
                  sb.Append("<th>");
                  sb.Append(GetPropertyName(Activator.CreateInstance<T>(), column));
                  sb.Append("</th>");
              }
              sb.AppendLine("</tr>");
              sb.AppendLine("<tbody>");
      
              foreach (var item in list)
              {
                  sb.AppendLine("<tr>");
      
                  foreach (var column in columns)
                  {
                      var func = column.Compile();
                      sb.Append("<td>");
                      sb.Append(func(item));
                      sb.Append("</td>");
      
                  }
                  sb.AppendLine("</tr>");
              }
              sb.AppendLine("</tbody>");
              sb.AppendLine("</table>");
      
              return sb.ToString();
          }
      
      
      
      
      
      
          public string GetPropertyName<TSource, TProperty>(TSource source, Expression<Func<TSource, TProperty>> propertyLambda)
          {
              Type type = typeof(TSource);
      
      
              var expressionBody = propertyLambda.Body;
              if (expressionBody is UnaryExpression expression && expression.NodeType == ExpressionType.Convert)
              {
                  expressionBody = expression.Operand;
              }
      
      
              MemberExpression member = (MemberExpression)expressionBody;
              if (member == null)
                  return "";
      
              PropertyInfo propInfo = member.Member as PropertyInfo;
              if (propInfo == null)
                  return "";
      
              if (type != propInfo.ReflectedType &&
                  !type.IsSubclassOf(propInfo.ReflectedType)) return "";
      
              return propInfo.Name;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-16
        • 1970-01-01
        • 2020-07-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-01-26
        相关资源
        最近更新 更多