NPOI解析Excel的例子很多,一般是针对单行表头,输出DataTable的,如果遇到多行表头,输出泛型对象的,要稍微复杂一些,参考了一篇博文,理解后进行了简化,支持了第五篇的合并单元格读取(参考地址:https://www.cnblogs.com/csqb-511612371/p/4891492.html 与 https://www.cnblogs.com/csqb-511612371/p/4895548.html)原文一共6篇,写得很详细。
定义:
public class ExcelHelper
{
public List<T> ExcelToCollection<T>(string excelPath, Dictionary<string, string> headerPropDict, string sheetName, int indexHeaderStart, int indexHeaderEnd)
{
var result = new List<T>();
if (string.IsNullOrEmpty(excelPath) || !File.Exists(excelPath))
{
throw new FileNotFoundException();
}
if (indexHeaderStart < 0 || indexHeaderEnd < 0 || indexHeaderStart > indexHeaderEnd)
{
throw new ArgumentException();
}
try
{
using (Stream fileStream = new FileStream(excelPath, FileMode.Open))
{
IWorkbook workbook;
if (excelPath.EndsWith(".xls"))
{
workbook = new HSSFWorkbook(fileStream);
}
else if (excelPath.EndsWith(".xlsx"))
{
workbook = new XSSFWorkbook(fileStream);
}
else
{
throw new Exception("格式不支持");
}
ISheet sheet;
if (!string.IsNullOrEmpty(sheetName))
{
sheet = workbook.GetSheet(sheetName);
if (sheet == null)
{
throw new Exception("sheet name Error");
}
}
else
{
sheet = workbook.GetSheetAt(0);
}
var headerDict = GetHeaderIndexDict(sheet, indexHeaderStart, indexHeaderEnd);
if (headerDict.Count == 0)
{
throw new Exception("未读取到表头,请检查Excel模板与调用入参");
}
for (int i = indexHeaderEnd; i <= sheet.LastRowNum; i++)
{
// 产生一个新的泛型对象
var model = Activator.CreateInstance<T>();
// 是否为空行
bool isEmptyRow = true;
IRow dataRow = sheet.GetRow(i);
int cellCount = headerDict.Count;
if (dataRow != null)
{
// 循环列数据
for (int j = dataRow.FirstCellNum; j < cellCount; j++)
{
var propertyName = headerPropDict[headerDict[j]];
PropertyInfo prop = model.GetType().GetProperty(propertyName);
ICell cell = dataRow.GetCell(j);
var value = GetCellValue<T>(sheet, cell, j, i, indexHeaderEnd, result, prop.Name);
if (!string.IsNullOrEmpty(value))
{
// 赋值
switch (prop.PropertyType.FullName)
{
case "System.Double":
if (double.TryParse(value, out double valueDecimal))
{
prop.SetValue(model, valueDecimal, null);
}
break;
case "System.Int16":