public static string HtmlList(IEnumerable items, HtmlDumpOptions options) { if (options == null) { options = new HtmlDumpOptions(); } if (items is IDictionary <string, object> single) { items = new[] { single } } ; var depth = options.Depth; var childDepth = options.ChildDepth; options.Depth += 1; try { var parentClass = options.ClassName; var childClass = options.ChildClass; var className = ((depth < childDepth ? parentClass : childClass ?? parentClass) ?? options.Defaults.GetDefaultTableClassName()); var headerStyle = options.HeaderStyle; var headerTag = options.HeaderTag ?? "th"; var sbHeader = StringBuilderCache.Allocate(); var sbRows = StringBuilderCacheAlt.Allocate(); List <string> keys = null; foreach (var item in items) { if (item is IDictionary <string, object> d) { if (keys == null) { keys = options.Headers?.ToList() ?? EnumerableExtensions.AllKeysWithDefaultValues(items); sbHeader.Append("<tr>"); foreach (var key in keys) { sbHeader.Append('<').Append(headerTag).Append('>'); sbHeader.Append(ViewUtils.StyleText(key, headerStyle)?.HtmlEncode()); sbHeader.Append("</").Append(headerTag).Append('>'); } sbHeader.Append("</tr>"); } sbRows.Append("<tr>"); foreach (var key in keys) { var value = d[key]; if (ReferenceEquals(value, items)) { break; // Prevent cyclical deps like 'it' binding } sbRows.Append("<td>"); if (!isComplexType(value)) { sbRows.Append(GetScalarHtml(value, options.Defaults)); } else { var htmlValue = HtmlDump(value, options); sbRows.Append(htmlValue.AsString()); } sbRows.Append("</td>"); } sbRows.Append("</tr>"); } } var isEmpty = sbRows.Length == 0; if (isEmpty && options.CaptionIfEmpty == null) { return(string.Empty); } var htmlHeaders = StringBuilderCache.ReturnAndFree(sbHeader); var htmlRows = StringBuilderCacheAlt.ReturnAndFree(sbRows); var sb = StringBuilderCache.Allocate(); sb.Append("<table"); if (options.Id != null) { sb.Append(" id=\"").Append(options.Id).Append("\""); } if (!string.IsNullOrEmpty(className)) { sb.Append(" class=\"").Append(className).Append("\""); } sb.Append(">"); var caption = options.Caption; if (isEmpty) { caption = options.CaptionIfEmpty; } if (caption != null && !options.HasCaption) { sb.Append("<caption>").Append(caption.HtmlEncode()).Append("</caption>"); options.HasCaption = true; } if (htmlHeaders.Length > 0) { sb.Append("<thead>").Append(htmlHeaders).Append("</thead>"); } if (htmlRows.Length > 0) { sb.Append("<tbody>").Append(htmlRows).Append("</tbody>"); } sb.Append("</table>"); var html = StringBuilderCache.ReturnAndFree(sb); return(html); } finally { options.Depth = depth; } }