You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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解析:

  1. 先安装Nuget依赖
Install-Package HtmlAgilityPack
  1. 实现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();
}
  1. 修改行赋值逻辑,同时设置单元格自动换行保证格式正常显示
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 12:15:03