通过 SpreadSheetGear 将 .xlsx 读取到 dto

Posted

技术标签:

【中文标题】通过 SpreadSheetGear 将 .xlsx 读取到 dto【英文标题】:Read .xlsx to dto via SpreadSheetGear 【发布时间】:2021-06-20 05:46:13 【问题描述】:

我想在 .Net Core 3.1 控制台应用程序中使用电子表格将 exel 文件读取到自定义模型(映射它!)。 我的 excel 文件如下所示:

| Customers | Sales Item | Sale Date  | Contact | Quantity |
| IBM       | Keyboard   | 28-10-2011 |         | 2        |
| Logitech  | Mouse      | 27-09-2011 | joe     | 5        |

DTO

public class CustomersDto

    public string Customers  get; set; 

    public string SalesItem  get; set; 

    public DateTime Date  get; set; 

    public string Contact   get; set; 

    public int Quantity get; set; 

通用代码:

private static void Main(string[] args)
    
        var workbook =
            Factory.GetWorkbook(
                @"D:\MyFiles\SellersCollections.xlsx");
        var worksheet = workbook.Worksheets[0];

        var cells = worksheet.UsedRange;
        List<CustomersDto> test = new List<CustomersDto>();
        CustomersDto dto = new CustomersDto();

        for (var i = 0; i < cells.RowCount; i++)
        
            if (i == 1) // skip header (0)
            
                for (var j = 0; j < cells.ColumnCount; j++)
                
                   Console.WriteLine(cells[i,j].Value);

                   // What should I do HERE to MAP to DTO
                   // Something like:
                   // dto.Customers = cells[i, j]; ???
                
            
        
        
        Console.ReadLine();
    

有没有办法将cells[i, j]IRange 映射到模型类?

【问题讨论】:

【参考方案1】:

SpreadsheetGear 中没有任何内置映射或绑定功能可以为您执行此操作,因此您需要构建自己的例程来执行此操作。如果您的工作簿和 DTO 定义明确且事先已知,我将保留外部“行循环”,但删除内部“列循环”并手动索引每个列单元格值,并根据需要将其放入相应的 DTO 属性中。当然,这意味着您的代码对每一列中包含的信息及其在 DTO 中的相应属性有一些了解。如果你需要一些更动态的东西,你必须建立一个更精细的系统,这超出了这里的回答范围。

在获取单元格值方面,您可能需要查看两个主要的 IRange 属性:

IRange.Value - 此属性的类型为 object,并返回单元格的“原始”值。例如double1.23booltruestring"abc"。 IRange.Text - 这是string 类型并返回单元格的“格式化”值——这意味着它采用 IRange.Value 并将该单元格的数字格式 (IRange.NumberFormat) 应用于该值并返回结果字符串。换句话说,这将返回您从 Excel 的 UI 和/或 SpreadsheetGear 的 WorkbookView UI 控件看到的内容。因此,如果一个单元格的 IRange.Value 为双 1.23,但被格式化为显示 4 个小数位 (IRange.NumberFormat == "0.0000"),则 IRange.Text 将返回字符串 "1.2300"。假设具有booltrue 的单元格的General NumberFormat,IRange.Text 将返回字符串"TRUE"。等等。

需要特别考虑日期。这是因为 Excel 中的日期、时间和日期时间在内部存储为序列数字双精度值,值 1.0 为 1900 年 1 月 1 日,2.0 为 1900 年 1 月 2 日,今天(2021 年 3 月 23 日)为 44278 等;该值的小数部分表示当天的时间(即,0.5 是中午,0.625 是下午 3:00)。单元格显示为“日期”或“时间”或“日期时间”的事实纯粹是应用于单元格的 IRange.NumberFormat 的函数(“m/d/yyyy”或“h:mm:ss”,等等)。如果您想从单元格中获取实际的 DateTime 对象,则需要使用 IWorkbook.NumberToDateTime(double serialDate) 辅助方法来执行此操作。

因此,归根结底,您的例程可能如下所示:

private static void Main(string[] args)

    var workbook =
    Factory.GetWorkbook(
        @"D:\MyFiles\SellersCollections.xlsx");
    var worksheet = workbook.Worksheets[0];

    var cells = worksheet.UsedRange;
    List<CustomersDto> dtoList = new List<CustomersDto>();

    for (var i = 0; i < cells.RowCount; i++)
    
        if (i == 1) // skip header (0)
        
            CustomersDto dto = new CustomersDto();

            // First two columns appear to be text, using IRange.Text should be fine
            // but could probably use IRange.Value as well.
            dto.Customers = cells[i, 0].Text;
            dto.SalesItem = cells[i, 1].Text;

            // Need to convert serial date stored in cell values to actual DateTimes.
            // Use IRange.Value and cast to double (may need to introduce some value 
            // type checking here if other values or empty cell values are allowed.  
            // See IRange.ValueType to detect what kind of value is stored in a given 
            // cell).
            double serialDate = (double)cells[i, 2].Value;
            // Convert serial date to a true DateTime object
            DateTime dateTime = workbook.NumberToDateTime(serialDate);
            dto.Date = dateTime;

            // Contact column appears to be text, so IRange.Text should suffice.
            dto.Contact = cells[i, 3].Text;

            // Appears to be a number so cast to double (see note above about possible
            // value type checking) and then cast again to int.
            dto.Quantity = (int)(double)cells[i, 4].Value;

            // Add to list
            dtoList.Add(dto);
        
    

    Console.ReadLine();

【讨论】:

非常感谢!优秀的答案!我有问题要问你,有什么方法可以检测电子表格中的标题吗? 不幸的是,没有内置功能可以做到这一点。标题行很可能包含所有基于文本的单元格值,因此遍历第一行并确保所有单元格都是IRange.ValueType == ValueType.Text 可能是执行此操作的一种方法。当然,“标题”本质上可能是一个非常主观的东西,试图弄清楚,例如,如果你的表格数据都是没有标题的文本怎么办?这样的例程会误报,因此您可能需要考虑这样的事情,也许根据您对传入数据可能拥有的任何特定领域的见解应用进一步的猜测。

以上是关于通过 SpreadSheetGear 将 .xlsx 读取到 dto的主要内容,如果未能解决你的问题,请参考以下文章

C#通过WebAPI控制器将Excel xls发送到js并将其保存为文件

将 xls 转换为 xlsx 并删除旧文件

在 iOS 上将数据导出到 XLS(不是通过 CSV)

VBA 将 Excel 文件从 Access 保存为 .xls

使用php将xls转换为html [重复]

如何将上传的 CSV 或 XLS 文件中的数据自动导入 Google 表格