使用ClosedXML导出DataTable到Excel时HTML标签未转成可读格式如何解决
ClosedXML导出Excel处理HTML内容格式化方案
问题根因
你当前代码仅对表头字段调用了StripHTML处理HTML标签,行数据直接使用原始值输出,同时普通的HTML标签移除方法无法保留列表的序号、缩进等格式属性,所以无法达到预期效果。
方案1:仅移除HTML标签,无需保留列表格式
如果不需要保留列表序号、缩进,直接对行数据也调用StripHTML方法即可,修改遍历行数据的赋值逻辑:
// 原来的行赋值代码 // worksheet.Cell(rowno, colno).Value=(dr[column.ColumnName].ToString()); // 修改为 worksheet.Cell(rowno, colno).Value = StripHTML(dr[column.ColumnName].ToString());
如果没有现成的StripHTML方法,可以用如下正则实现:
public static string StripHTML(string input) { if (string.IsNullOrEmpty(input)) return string.Empty; return System.Text.RegularExpressions.Regex.Replace(input, "<.*?>", string.Empty); }
方案2:保留列表序号、缩进的格式化效果
要达到你期望的带序号、层级缩进的效果,需要解析HTML的DOM结构,处理列表节点属性,生成对应格式的纯文本,推荐用HtmlAgilityPack包处理HTML解析:
- 先安装Nuget依赖
Install-Package HtmlAgilityPack
- 实现HTML转格式化文本的方法
using HtmlAgilityPack; using System.Text; public static string ConvertHtmlToFormattedText(string html) { if (string.IsNullOrWhiteSpace(html)) return string.Empty; var doc = new HtmlDocument(); doc.LoadHtml(html); var sb = new StringBuilder(); ProcessNode(doc.DocumentNode, sb, 0); return sb.ToString().Trim(); } private static void ProcessNode(HtmlNode node, StringBuilder sb, int indentLevel) { switch (node.Name.ToLower()) { case "ol": // 处理有序列表的起始序号、序号类型 int start = 1; var startAttr = node.Attributes["start"]; if (startAttr != null && int.TryParse(startAttr.Value, out int s)) start = s; char listType = '1'; var typeAttr = node.Attributes["type"]; if (typeAttr != null && typeAttr.Value.Length > 0) listType = char.ToLower(typeAttr.Value[0]); int itemIndex = start; foreach (var child in node.ChildNodes.Where(n => n.Name.ToLower() == "li")) { // 生成对应格式的序号 string prefix = listType switch { 'a' => ((char)('a' + itemIndex - 1)).ToString(), 'i' => ToRoman(itemIndex).ToLower(), 'I' => ToRoman(itemIndex), _ => itemIndex.ToString() }; // 加缩进 sb.Append(' ', indentLevel * 2); sb.Append(prefix).Append(". "); // 处理li内部的内容和子节点 foreach (var liChild in child.ChildNodes) { ProcessNode(liChild, sb, indentLevel + 1); } sb.AppendLine(); itemIndex++; } break; case "li": // li节点已经在父ol/ul里处理,这里跳过避免重复 break; case "#text": // 文本节点直接追加 var text = node.InnerText.Trim(); if (!string.IsNullOrEmpty(text)) sb.Append(text); break; default: // 其他节点递归处理子节点 foreach (var child in node.ChildNodes) { ProcessNode(child, sb, indentLevel); } break; } } // 可选:罗马数字转换方法,需要处理i类型的有序列表时添加 private static string ToRoman(int number) { if (number < 1 || number > 3999) return number.ToString(); int[] values = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 }; string[] symbols = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" }; var sb = new StringBuilder(); for (int i = 0; i < values.Length; i++) { while (number >= values[i]) { number -= values[i]; sb.Append(symbols[i]); } } return sb.ToString(); }
- 修改行赋值逻辑,同时设置单元格自动换行保证格式正常显示
foreach (DataColumn column in dt.Columns) { string cellValue = ConvertHtmlToFormattedText(dr[column.ColumnName].ToString()); var cell = worksheet.Cell(rowno, colno); cell.Value = cellValue; // 设置自动换行,保证缩进和换行正常显示 cell.Style.Alignment.WrapText = true; ++colno; } // 导出完成后可统一调整列宽适配内容 worksheet.Columns().AdjustToContents();
修改后的完整核心代码
public static MemoryStream ConvertDataTableToExcel(DataTable dt, string name, bool addColumnNames = true) { var stream = new MemoryStream(); if (dt != null) { using (var workbook = new XLWorkbook()) { var worksheet = workbook.Worksheets.Add(name); var rowno = 1; var colno = 1; if (addColumnNames) { foreach (DataColumn column in dt.Columns) { worksheet.Cell(rowno, colno).Value = StripHTML(column.ColumnName); colno++; } } foreach (DataRow dr in dt.Rows) { ++rowno; colno = 1; foreach (DataColumn column in dt.Columns) { // 替换为你需要的处理方法,仅删标签用StripHTML,要保留格式用ConvertHtmlToFormattedText string cellValue = ConvertHtmlToFormattedText(dr[column.ColumnName].ToString()); var cell = worksheet.Cell(rowno, colno); cell.Value = cellValue; cell.Style.Alignment.WrapText = true; ++colno; } } worksheet.Columns().AdjustToContents(); workbook.SaveAs(stream); } } stream.Position = 0; // 重置流位置,避免后续读取为空 return stream; }
内容的提问来源于stack exchange,提问作者teja0809
相关产品推荐
相关产品推荐

