从 Excel 工作表中提取 .NET 对象
描述了一种通用方法,
引言
本文演示了一种从 Excel 工作表或 Xml SpreadSheet 中获取 .NET 对象列表的快速方法,而不是 .NET Dataset
/Datatable
。此方法处理对象的 Enum 类型属性以及工作表数据不是从第 1 行第 1 列开始的情况。有时,用户需要在他们的应用程序中使用 Excel 工作表文件中的强类型对象列表,本文将帮助您以一种简单快速的方式实现这一目标。
背景
几个月前,我得到了一些 Excel 文件(另存为 Xml SpreadSheet 2003 格式),需要从中提取数据到 .NET 对象列表。 数据提取基于工作表的标题行或数据列位置/索引。 在我的例子中,为了序列化来自不同 XML 的不同对象,需要每次都进行编码。 为了摆脱这个问题,以下通用方法将对我们有所帮助。
要求:运行演示代码
- .net framework 4.5 或更高版本
- 需要 visual studio 2015 或更高版本。
- 已在 (Microsoft Excel 2007) 上测试 *非强制
Using the Code
例如,假设我们有一个名为 Person
的 Class
:
public class Person {
public int ID { get; set; }
public string Name { get; set; }
public Nullable<System.DateTime> DateOfBirth { get; set; }
public string District {get; set;}
}
并且 Excel 文件(另存为 MS XML 格式 2003)看起来像这样
ExcelFile.xml
<?xml version="1.0"?>
<Workbook >
<Worksheet Name="Sheet1">
<Table>
<Row>
<Cell><Data Type="String">ID</Data></Cell>
<Cell><Data Type="String">Name</Data></Cell>
<Cell><Data Type="String">DofB</Data></Cell>
<Cell></Cell>
</Row>
<Row>
<Cell><Data Type="Number">1</Data></Cell>
<Cell><Data Type="String">A</Data></Cell>
<Cell><Data Type="DateTime">2010-09-28T00:00:00.000</Data></Cell>
<Cell><Data Type="String">District Z</Data></Cell>
</Row>
<Row>
<Cell><Data Type="Number">2</Data></Cell>
<Cell><Data Type="String">B</Data></Cell>
<Cell><Data Type="DateTime">2011-09-28T00:00:00.000</Data></Cell>
<Cell><Data Type="String">District Y</Data></Cell>
</Row>
</Table>
</Worksheet>
</Workbook>
从输入 XML 文件中,我们看到一些数据行。 第 1st 行(索引 = 0)表示标题行,其余行是 Person
的数据行。 要从 XML 文件中获取 Person List
步骤 1
将下载的 *ExcelToObjectConvertor 项目*添加到您的解决方案,或者将 ExcelToObjectConvertor.dll
添加到您的项目中。 然后在要使用它的文件中包含它的命名空间。
using ExcelToObjectConvertor;
调用 ExcelXMLConvertor.GetWortSheetsInfo
方法。 这将给出所有工作表列表。 此列表包含工作表名称、工作表 Id 和其他信息。
string filePath = @"your excel file path";
List<WorkSheetInfo> workSheetList = ExcelXMLConvertor.GetWortSheetsInfo(filePath);
or
//for the file upload from fileUpload or OpenFileDialog
List<WorkSheetInfo> workSheetList = ExcelXMLConvertor.GetWortSheetsInfo(PostedFile.InputStream);
从 workSheetList 中按名称或索引查找工作表,或者循环访问所有工作表以从中获取数据。
// Get data by workSheet name
WorkSheetInfo workSheet = workSheetList.Find(wh => wh.WorkSheetName == @"Sheet1");
//Update properties or Map object property to Column Name/Number of workSheet.
//get data from workSheet.
Or
// Get data from all workSheet
foreach (WorkSheetInfo workSheet in workSheetList)
{
//Update properties or Map object property to Column Name/Number of workSheet.
//get data from workSheet.
}
第二步
默认标题行索引为零 (HeaderRowIndex=0
)。 您可以通过调用来更改标题行索引
ExcelXMLConvertor.UpdateHeaderRowIndexProperty(workSheet.WorkSheetId, newheaderRowIndex);
默认情况下,它会将 Header
行中的 Header
文本映射到对象属性名称。 例如,Person.Name
映射到第 1st 个 <Row>
的第 2nd 个 <cell>
,Person.ID
映射到第 1st 个 <Row>
的第 1st 个 <cell>
<Row>
<Cell><Data Type="String">ID</Data></Cell> //-->1st cell , cell position or column index = 0
<cell><Data Type="String">Name</Data></cell> //-->2nd cell , cell position or column index = 1
...
</Row>
所有第 1st 个单元格数据将映射到 Person.ID
,第 2nd 个单元格数据将映射到 Person.Name
。
特殊情况
在我们的例子中,Person.DateOfBirth
无法映射到 XML 文件的 <Row>
节点的第 3rd 个 <Cell>
:
<Row>
...
<cell><Data Type="String">DofB</Data></cell> //-->3rd cell , cell position or column index = 2
...
</Row>
将 Person.DateOfBirth
映射到第 3rd 个 <cell>
节点,标题文本为 DofB
Person person = new Person();
ExcelXMLConvertor.MapPropertyNameToHeaderName(workSheet.WorkSheetId,
nameof(person.DateOfBirth), @"DofB");
所有 <row>
的最后一个 <cell>
节点都没有标题文本,因此它无法映射到任何类属性。 但是您可以将此 <cell>
节点与单元格位置或列索引映射,以便所有最后一个 <cell>
节点都映射到 Person.District
。 为此,将最后一个 <cell>
(index = 3
)映射到 Person.District
:
uint index = 3;
ExcelXMLConvertor.MapPropertyNameToColumnNumber(workSheet.WorkSheetId, nameof(person.District),index);
*如果没有标题行,则必须将所有属性与列号映射。
步骤 3
最后,调用方法 ExcelXMLConvertor.GetObjectList<T>(WorkSheetId)
// Get data by workSheet name
List<Person> personList = ExcelXMLConvertor.GetObjectList<Person>(workSheet.WorkSheetId);
Or
// Get data from all workSheet if all workSheets have same type's data.
List<Person> personList = new List<Person>();;
foreach (WorkSheetInfo workSheet in workSheetList)
{
personList.AddRange(ExcelXMLConvertor.GetObjectList<Person>(workSheet.WorkSheetId));
}
代码执行后,PersonList
将有两个 person
对象
PersonList --> {Person(1, A, 2010-09-28, District Z), Person(2, B, 2011-09-28, District Y)}
完整代码如下所示
using System;
using System.Xml;
using ExcelToObjectConvertor;
using System.Collections.Generic;
using System.IO;
using Excel = Microsoft.Office.Interop.Excel;
class Program
{
static void Main(string[] args)
{
string filePath = Path.GetFullPath(@"Your File path");
List<WorkSheetInfo> workSheetList= ExcelXMLConvertor.GetWortSheetsInfo(filePath);
WorkSheetInfo workSheet = workSheetList.Find(wh => wh.WorkSheetName == @"Sheet1");
Person person = new Person();
ExcelXMLConvertor.MapPropertyNameToHeaderName(workSheet.WorkSheetId,
nameof(person.DateOfBirth), @"DofB");
uint index = 3;
ExcelXMLConvertor.MapPropertyNameToColumnNumber(workSheet.WorkSheetId,
nameof(person.District),index);
//Result:
List<Person> personList = ExcelXMLConvertor.GetObjectList<Person>(workSheet.WorkSheetId);
}
}
使用示例 Web 应用程序
在我的演示项目中,我提供了一个示例 Web(aspx) 和控制台项目,以提供有关 ExcelToObjectConvertor 如何工作的想法。 以下图片将显示并解释它的工作原理。 运行演示 Web 项目,您会发现页面看起来像下图。
浏览您的 Excel 文件,然后单击“上传 Excel 文件”按钮上传它。 如果您不需要任何属性映射,只需从工作表下拉列表中选择一个工作表名称,从对象下拉列表中选择您要转换的对象名称(上图中未显示),然后单击“获取对象列表”按钮。 您将获得转换后的对象列表,该列表显示在页面底部。
标题行
默认情况下,第 1 行被检测为标题行,但用户可以从电子表格的前 5 行中选择一行作为标题行,从 First5Row 网格中选择。 如果没有标题行,则取消选中 HasHeader 复选框,并使用列号映射要加载数据的所有属性。 为此,选择列号单选按钮。 请参见下图
将属性映射到标题名称或单元格索引
从属性下拉列表中选择一个属性,从标题名称下拉列表中选择一个标题名称,然后单击“添加到映射对”按钮。 一个映射对将添加到映射对列表中。 您还可以通过选择“列号”单选按钮,插入一个索引(0 到 26),从“属性”下拉列表中选择适当的属性,然后单击“添加到映射对”按钮来将属性与列号/索引映射。
映射所有属性后,单击“获取对象列表”按钮。 请参见下面的示例输出
希望它能帮助一些人并减少工作量。
历史
- 2018 年 3 月 22 日:初始版本