开源 - Ideal库 - Excel帮助类,TableHelper实现(三)

news/2025/2/11 20:40:14/文章来源:https://www.cnblogs.com/hugogoos/p/18580410

书接上回,我们今天继续讲解实现对象集合与DataTable的相互转换。

01、把表格转换为对象集合

该方法是将表格的列名称作为类的属性名,将表格的行数据转为类的对象。从而实现表格转换为对象集合。同时我们约定如果类的属性设置了DescriptionAttribute特性,则特性值和表格列名一一对应,如果没有设置特性则取属性名称和列名一一对应。

同时我们需要约束类只能为结构体或类,而不能是枚举、基础类型、以及集合类型、委托、接口等。

类的类型校验成功后,我们还需要校验表格是否能转换为对象,即判断表格列名和类的属性名称或者Description特性值是否存在一致,如果没有一个表格列名和类的属性能对应上,则报表格列名无法映射至对象属性,无法完成转换异常。

当这些校验成功后,开始循环处理表格行记录,把每一行都转换为一个对象。

我们可以通过反射动态实例化对象,再通过反射对对象的属性动态赋值。因为我们的对象即支持类也支持结构体,因此这里面就会遇到一个技术问题,正常的property.SetValue方法并没有办法给结构体动态赋值。

这是因为结构体是值类型,而property.SetValue方法的参数都是object,因此这里面就涉及到装箱拆箱,因此SetValue是设置了装箱以后的对象,而并不能改变原对象。

而解决办法就是先把结构体赋值给object变量,然后对object变量进行SetValue设置值,最后再把object变量转为结构体。

下面我们一起看看具体实现代码:

//把表格转换为对象集合
//如果设置DescriptionAttribute,则将特性值作为表格的列名称
//否则将属性名作为表格的列名称
public static IEnumerable<T> ToModels<T>(DataTable dataTable)
{//T必须是结构体或类,并且不能是集合类型AssertTypeValid<T>();if (0 == dataTable.Rows.Count){return [];}//获取T所有可写入属性var properties = typeof(T).GetProperties().Where(u => u.CanWrite);//校验表格是否能转换为对象var isCanParse = IsCanMapDataTableToModel(dataTable, properties);if (!isCanParse){throw new NotSupportedException("The column name of the table cannot be mapped to an object property, and the conversion cannot be completed.");}var models = new List<T>();foreach (DataRow dr in dataTable.Rows){//通过反射实例化Tvar model = Activator.CreateInstance<T>();//把行数据映射到对象上if (typeof(T).IsClass){//处理T为类的情况MapRowToModel<T>(dr, model, properties);}else{//处理T为结构体的情况object boxed = model!;MapRowToModel<object>(dr, boxed, properties);model = (T)boxed;}models.Add(model);}return models;
}
//校验表格是否能转换为对象
private static bool IsCanMapDataTableToModel(DataTable dataTable, IEnumerable<PropertyInfo> properties)
{var isCanParse = false;foreach (var property in properties){//根据属性获取列名var columnName = GetColumnName(property);if (!dataTable.Columns.Contains(columnName)){continue;}isCanParse = true;}return isCanParse;
}
//把行数据映射到对象上
private static void MapRowToModel<T>(DataRow dataRow, T model, IEnumerable<PropertyInfo> properties)
{foreach (var property in properties){//根据属性获取列名var columnName = GetColumnName(property);if (!dataRow.Table.Columns.Contains(columnName)){continue;}//获取单元格值var value = dataRow[columnName];if (value != DBNull.Value){//给对象属性赋值property.SetValue(model, Convert.ChangeType(value, property.PropertyType));}}
}

我们做个简单的单元测试:

[Fact]
public void ToModels()
{//验证正常情况var table = TableHelper.Create<Student<double>>();var row1 = table.NewRow();row1[0] = "Id-11";row1[1] = "名称-12";row1[2] = 33.13;table.Rows.Add(row1);var row2 = table.NewRow();row2[0] = "Id-21";row2[1] = "名称-22";row2[2] = 33.23;table.Rows.Add(row2);var students = TableHelper.ToModels<Student<double>>(table);Assert.Equal(2, students.Count());Assert.Equal("Id-11", students.ElementAt(0).Id);Assert.Equal("名称-12", students.ElementAt(0).Name);Assert.Equal(33.13, students.ElementAt(0).Age);Assert.Equal("Id-21", students.ElementAt(1).Id);Assert.Equal("名称-22", students.ElementAt(1).Name);Assert.Equal(33.23, students.ElementAt(1).Age);
}

02、把对象集合转换为表格

该方法首先会调用根据对象创建表格方法得到一个空白表格,然后通过反射获取对象的所有属性,然后循环处理对象集合,把一个对象的所有属性值一个一个添加行的所有列中,这样就完成了一个对象映射成表的一行记录,直至所有对象转换完成即可得到一个表格。

代码如下:

//把对象集合转为表格
//如果设置DescriptionAttribute,则将特性值作为表格的列名称
//否则将属性名作为表格的列名称
public static DataTable ToDataTable<T>(IEnumerable<T> models, string? tableName = null)
{//创建表格var dataTable = Create<T>(tableName);if (models == null || !models.Any()){return dataTable;}//获取所有属性var properties = typeof(T).GetProperties().Where(u => u.CanRead);foreach (var model in models){//创建行var dataRow = dataTable.NewRow();foreach (var property in properties){//根据属性获取列名var columnName = GetColumnName(property);//填充行数据dataRow[columnName] = property.GetValue(model);}dataTable.Rows.Add(dataRow);}return dataTable;
}

进行如下单元测试:

[Fact]
public void ToDataTable()
{//验证正常情况var students = new List<Student<double>>();var student1 = new Student<double>{Id = "Id-11",Name = "名称-12",Age = 33.13};students.Add(student1);var student2 = new Student<double>{Id = "Id-21",Name = "名称-22",Age = 33.23};students.Add(student2);var table = TableHelper.ToDataTable<Student<double>>(students, "学生表");Assert.Equal("学生表", table.TableName);Assert.Equal(2, table.Rows.Count);Assert.Equal("Id-11", table.Rows[0][0]);Assert.Equal("名称-12", table.Rows[0][1]);Assert.Equal("33.13", table.Rows[0][2].ToString());Assert.Equal("Id-21", table.Rows[1][0]);Assert.Equal("名称-22", table.Rows[1][1]);Assert.Equal("33.23", table.Rows[1][2].ToString());
}

03、把一维数组作为一列转换为表格

该方法比较简单就是把一个一维数组作为一列数据创建一张表格,同时可以选择是否填写表名和列名。具体代码如下:

//把一维数组作为一列转换为表格
public static DataTable ToDataTableWithColumnArray<TColumn>(TColumn[] array, string? tableName = null, string? columnName = null)
{var dataTable = new DataTable(tableName);//创建列dataTable.Columns.Add(columnName, typeof(TColumn));//添加行数据foreach (var item in array){var dataRow = dataTable.NewRow();dataRow[0] = item;dataTable.Rows.Add(dataRow);}return dataTable;
}

单元测试如下:

[Fact]
public void ToDataTableWithColumnArray()
{//验证正常情况var columns = new string[] { "A", "B" };var table = TableHelper.ToDataTableWithColumnArray<string>(columns, "学生表");Assert.Equal("学生表", table.TableName);Assert.Equal("Column1", table.Columns[0].ColumnName);Assert.Equal(2, table.Rows.Count);Assert.Equal("A", table.Rows[0][0]);Assert.Equal("B", table.Rows[1][0]);table = TableHelper.ToDataTableWithColumnArray<string>(columns, "学生表", "列");Assert.Equal("列", table.Columns[0].ColumnName);
}

04、把一维数组作为一行转换为表格

该方法也比较简单就是把一个一维数组作为一行数据创建一张表格,同时可以选择是否填写表名。具体代码如下:

//把一维数组作为一行转换为表格
public static DataTable ToDataTableWithRowArray<TRow>(TRow[] array, string? tableName = null)
{var dataTable = new DataTable(tableName);//创建列for (var i = 0; i < array.Length; i++){dataTable.Columns.Add(null, typeof(TRow));}//添加行数据var dataRow = dataTable.NewRow();for (var i = 0; i < array.Length; i++){dataRow[i] = array[i];}dataTable.Rows.Add(dataRow);return dataTable;
}

05、行列转置

该方法是指把DataTable中的行和列互换,就是行的数据变成列,列的数据变成行。如下图示例:

这个示例转换,第一个表格中列名并没有作为数据进行转换,因此我们会提供一个可选项参数用来指示要不要把类目作为数据进行转换。

整个方法实现逻辑也很简单,就是以原表格行数为列数创建一个新表格,然后在循环处理原表格列,并把原表格一列数据填充至新表格的一行数据中,直至原表格所有列处理完成则完成行列转置。具体代码如下:

//行列转置
public static DataTable Transpose(DataTable dataTable, bool isColumnNameAsData = true)
{var transposed = new DataTable(dataTable.TableName);//如果列名作为数据,则需要多加一列if (isColumnNameAsData){transposed.Columns.Add();}//转置后,行数即为新的列数for (int i = 0; i < dataTable.Rows.Count; i++){transposed.Columns.Add();}//以列为单位,一次处理一列数据for (var column = 0; column < dataTable.Columns.Count; column++){//创建新行var newRow = transposed.NewRow();//如果列名作为数据,则先把列名加入第一列if (isColumnNameAsData){newRow[0] = dataTable.Columns[column].ColumnName;}//把一列数据转为一行数据for (var row = 0; row < dataTable.Rows.Count; row++){//如果列名作为数据,则行数据从第二列开始填充var rowIndex = isColumnNameAsData ? row + 1 : row;newRow[rowIndex] = dataTable.Rows[row][column];}transposed.Rows.Add(newRow);}return transposed;
}

下面进行简单的单元测试:

[Fact]
public void Transpose_ColumnNameAsData()
{DataTable originalTable = new DataTable("测试");originalTable.Columns.Add("A", typeof(string));originalTable.Columns.Add("B", typeof(int));originalTable.Columns.Add("C", typeof(int));originalTable.Rows.Add("D", 1, 2);//列名作为数据的情况var table = TableHelper.Transpose(originalTable, true);Assert.Equal(originalTable.TableName, table.TableName);Assert.Equal("Column1", table.Columns[0].ColumnName);Assert.Equal("Column2", table.Columns[1].ColumnName);Assert.Equal(3, table.Rows.Count);Assert.Equal("A", table.Rows[0][0]);Assert.Equal("D", table.Rows[0][1]);Assert.Equal("B", table.Rows[1][0]);Assert.Equal("1", table.Rows[1][1].ToString());Assert.Equal("C", table.Rows[2][0]);Assert.Equal("2", table.Rows[2][1].ToString());
}

:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Ideal

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/844824.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

信息安全概论复习3

保密与安全文档保密密级术语密码体制 单钥密码体制(对称密码体制)加密方式双钥密码体制(非对称密码体制)(公钥体制)特点

优化企业博客内容:策略与ai工具的应用

在数字化营销日益重要的今天,企业博客不仅是品牌传播的重要渠道,也是吸引潜在客户、建立品牌权威性的关键工具。然而,要让企业博客真正发挥作用,关键在于内容的优化。本文将探讨优化企业博客内容的策略,并详细介绍如何利用HelpLook平台来实施这些策略,以提升博客的吸引力…

攻防世界-Decrypt-the-Message

一、题目 收到一首英文诗歌和一段密文,要求很简单,就是解密这个密文二、解题 1、背景知识PoemCode参考文章:https://blog.csdn.net/xiao__1bai/article/details/1202504522、解密 了解加密原理即可,解密过程很复杂,可以直接用现成的脚本脚本地址:https://github.com/abpoly…

请问PbootCMS 登录失败及表单提交校验失败的解决方案

在使用 PbootCMS 时,可能会遇到登录失败、表单提交校验失败等问题。以下是针对这些常见问题的详细解决方案。 一、登录失败或表单提交校验失败 描述:用户尝试登录时失败。 表单提交时校验失败。解决方案:检查服务器环境:确保服务器环境满足 PbootCMS 的最低要求。 检查 PHP 版…

# 学期(2024-2025-1) 学号(20241420) 《计算机基础与程序设计》第10周学习总结

学期(2024-2025-1) 学号(20241420) 《计算机基础与程序设计》第10周学习总结 作业信息这个作业属于哪个课程 <班级的链接>(2024-2025-1-计算机基础与程序设计)这个作业要求在哪里 <作业要求的链接>(如2024-2025-1计算机基础与程序设计第一周作业)这个作业的目…

关于用户反馈到产品需求的思考

1 前言 在数字产品的世界中,用户反馈不仅是检验产品成功与否的试金石,更是推动产品迭代与创新的关键驱动力。作为项目负责人,我们深知每一个反馈背后蕴含的用户需求与期望。最近刚好看到了@玉伯大佬写的文章从用户反馈到产品需求的思考,深受启发,结合这一年半的工作经验,…

2024年11月文章一览

2024年11月编程人总共更新了21篇文章: 1.2024年10月文章一览 2.《使用Gin框架构建分布式应用》阅读笔记:p307-p392 3.《使用Gin框架构建分布式应用》阅读笔记:p393-p437 4.《使用Gin框架构建分布式应用》读后感 5.《Django 5 By Example》阅读笔记:p1-p16 6.《Django 5 By …

请问帝国CMS动态页面生成静态页面失败怎么办

检查生成设置:进入后台的“系统设置” -> “生成设置”,检查静态页面生成设置是否正确。 确保生成模式选择正确,例如“只生成首页”、“生成所有页面”等。检查模板文件:确保模板文件中没有语法错误,特别是PHP代码部分。 检查模板文件路径是否正确。检查文件权限:确保…

请问宝塔安装好后网址打不开的可能原因及解决方法

检查域名解析确认域名是否正确解析到服务器的IP地址。 使用 ping 命令测试域名解析是否正确,例如:ping yourdomain.com。检查服务器网络确认服务器的网络连接是否正常。 尝试从服务器内部访问外部网站,确保网络畅通。检查防火墙设置确认服务器的防火墙设置是否允许HTTP/HTTP…

宝塔系统安装网站后打不开的解决方法及排查步骤

1. 检查网络连接确认服务器网络是否正常:从服务器上尝试 ping 外部网站,确保网络畅通。 例如:ping 域名 或 ping 你的网址2. 检查宝塔面板状态登录服务器,检查面板是否正常运行:使用命令 bt 进入宝塔面板管理界面。 如果面板未运行,尝试启动面板:bt start3. 检查网站配置…

2.C基础

1.注释单行注释 // 多行注释 /* */ 程序运行后生成的 .obj文件里不带注释的内容/*include 表示包含 std :标准的 i: 输入Input o:输出Output */ #include <stdio.h> //预处理//int 表示程序运行的结果是整数 与return 0相呼应 //main() 函数是程序的主入口 固定指定为…

昆工25届817信号与系统模拟考试

--吴大正信号与线性系统分析昆工昆明理工大学通信工程817信号与系统考研信号与信息处理通信与信息系统通信工程F002通信核心课程综合通信工程(含宽带网络、移动通信等)