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

书接上回,我们今天继续讲解实现对象集合与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.rhkb.cn/news/482275.html

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

相关文章

基于DHCP,ACL的通信

该问题为华为的学习资料 1.首先把所有的PC机全部设置为DHCP 2.配置地址 3.ospf 4.dhcp 5.acl AR1 dhcp en interface GigabitEthernet0/0/0ip address 192.168.1.254 255.255.255.0 dhcp select global interface GigabitEthernet0/0/1ip address 10.1.12.1 255.255.255.…

基于深度学习的卷积神经网络十二生肖图像识别系统(PyQt5界面+数据集+训练代码)

本研究提出了一种基于深度学习的十二生肖图像识别系统&#xff0c;旨在利用卷积神经网络&#xff08;CNN&#xff09;进行图像分类&#xff0c;特别是十二生肖图像的自动识别。系统的核心采用了两种经典的深度学习模型&#xff1a;ResNet50和VGG16&#xff0c;进行图像的特征提…

探索温度计的数字化设计:一个可视化温度数据的Web图表案例

随着科技的发展&#xff0c;数据可视化在各个领域中的应用越来越广泛。在温度监控和展示方面&#xff0c;传统的温度计已逐渐被数字化温度计所取代。本文将介绍一个使用Echarts库创建的温度计Web图表&#xff0c;该图表通过动态数据可视化展示了温度值&#xff0c;并通过渐变色…

Attention显存统计与分析

Attention显存估计 简单的Attention函数 import torch import torch.nn as nn import einops class Attention(nn.Module):def __init__(self, dim, num_heads8, qkv_biasFalse, qk_scaleNone, attn_drop0., proj_drop0.):super().__init__()self.num_heads num_headshead_d…

[MacOS] [kubernetes] MacOS玩转虚拟化最佳实践

❓ 为什么不在MacOS本机安装呢&#xff1f;因为M系列芯片是Arm架构&#xff0c;与生产环境或者在本地调试时候&#xff0c;安装虚拟镜像和X86不同&#xff0c;造成不必要的切换环境的额外成本&#xff0c;所以在虚拟化的x86调试 步骤 & 详情 一: 安装OrbStack & 并配置…

Unity世界坐标转屏幕坐标报错解决办法。

问题描述&#xff0c;如果你在脚本中尝试使用Camera.转换世界坐标的时候发现点不出来&#xff0c;可以点到你的Camera这个方法看能否跳转&#xff0c;如果能够跳转&#xff0c;并且这个脚本是你自己写的&#xff0c;那么恭喜你&#xff0c;下面就是解决办法&#xff0c;直接将C…

系统监控——分布式链路追踪系统

摘要 本文深入探讨了分布式链路追踪系统的必要性与实施细节。随着软件架构的复杂化&#xff0c;传统的日志分析方法已不足以应对问题定位的需求。文章首先解释了链路追踪的基本概念&#xff0c;如Trace和Span&#xff0c;并讨论了其基本原理。接着&#xff0c;文章介绍了SkyWa…

IAR中编译下载未下载问题

第一张图片是正常下载&#xff0c;第二张未正常下载。经过查看download选项发现 启用了 suppress download &#xff08;禁用下载)

【UE5 C++】判断两点连线是否穿过球体

目录 前言 原理 代码 测试 结果 前言 通过数学原理判断空间中任意两点的连线是否穿过球体&#xff0c;再通过射线检测检验算法的正确性。 原理 &#xff08;1&#xff09;设球体球心的坐标为 &#xff0c;半径为r&#xff1b; &#xff08;2&#xff09;设线段中A点的坐…

网络安全之IP伪造

眼下非常多站点的涉及存在一些安全漏洞&#xff0c;黑客easy使用ip伪造、session劫持、xss攻击、session注入等手段危害站点安全。在纪录片《互联网之子》&#xff08;建议搞IT的都要看下&#xff09;中。亚伦斯沃茨&#xff08;真实人物&#xff0c;神一般的存在&#xff09;涉…

《运放秘籍》第二部:仪表放大器专项知识点总结

一、差分放大器与仪表放大器的讨论 1.1. 仪放的前世今生——差分放大器原理&#xff1f; 1.2. 差分放大的原理 1.3. 差分放大器检测电流 1.4. 差分放大器端一&#xff1a;输入阻抗 1.5. 差分放大器端二&#xff1a;共模抑制比 1.6. 为什么关注输入阻抗&#xff1f;共模抑…

AJAX一、axios使用,url组成(协议,域名,资源路径)查询参数和化简,错误处理,请求/响应报文,状态码,接口文档,

一、AJAX是什么 概念 &#xff1a; AJAX是一种与服务器&#xff08;后端&#xff09;通信的技术 二、请求库axios的基本用法 1导包 2使用 // 1. 发请求 axios({ url: 请求地址 }).then(res > { // 2.接收并使用数据 }) <body><p class"province"…

关于ConstarintLayout有关的点

目录 一、概述 二、过程。 1、介绍 主要特点 关键概念 使用示例 总结 2、我遇到的问题 问题&#xff1a; 可能的原因&#xff1a; 结论 一、概述 在学习过程中&#xff0c;发现对ConstarintLayout理解不够到位&#xff0c;下面是发现并解决问题过程。 二、过程。 1…

【UE5 C++课程系列笔记】06——定时器的基本使用

目标 使用定时器每秒调用函数打印一句日志信息&#xff0c;并在调用一定次数后停止定时器。 步骤 1. 新建一个Actor类&#xff0c;这里命名为 2. 先在“TimerActor.cpp”中获取定时器管理器 使用定时器管理器来设置定时器&#xff0c;该定时器在2s后启动&#xff0c;然后每…

用c语言完成俄罗斯方块小游戏

用c语言完成俄罗斯方块小游戏 这估计是你在编程学习过程中的第一个小游戏开发&#xff0c;怎么说呢&#xff0c;在这里只针对刚学程序设计的学生&#xff0c;就是说刚接触C语言没多久&#xff0c;有一点功底的学生看看&#xff0c;简陋的代码&#xff0c;简陋的实现&#xff0…

iQOO Neo10系列携三大蓝科技亮相,性能与续航全面升级

11月29日&#xff0c;iQOO Neo10系列正式登场。作为iQOO Neo系列的最新力作&#xff0c;Neo10系列不仅延续了该系列一贯的“双芯”特色&#xff0c;更在性能、续航、屏幕、影像等多个方面实现了全面升级&#xff0c;为用户带来前所未有的使用体验。此次发布的Neo10系列共有两款…

springboot信息化在线教学平台的设计与实现(代码+数据库+LW)

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了信息化在线教学平台的开发全过程。通过分析信息化在线教学平台管理的不足&#xff0c;创建了一个计算机管理信息化在线教学平台的方案。文章介绍了信息化在线教…

DataX实战|使用Python 构建简易的DataX数据血缘工具(一)

导读&#xff1a; 在这篇文章中&#xff0c;我介绍了如何使用 Python 构建简易的 DataX 数据血缘工具&#xff0c;以便解决 DataXWeb 在查询表上下游关系时的不足。 选择 Flask 作为框架&#xff0c;利用 DataXWeb 的元数据 job_info 表和 job_json&#xff0c;通过 JSON 解析…

Android 14之HIDL转AIDL通信

Android 14之HIDL转AIDL通信 1、interface接口1.1 接口变更1.2 生成hidl2aidl工具1.3 执行hidl2aidl指令1.4 修改aidl的Android.bp文件1.5 创建路径1.6 拷贝生成的aidl到1和current1.7 更新与冻结版本1.8 编译模块接口 2、服务端代码适配hal代码修改2.1 修改Android.bp的hidl依…

51c视觉~YOLO~合集4

我自己的原文哦~ https://blog.51cto.com/whaosoft/12512597 1、Yolo8 1.1、检测PCB元件 技术世界正在以惊人的速度发展&#xff0c;而这种转变的核心是一个革命性的工具 — 计算机视觉。它最有趣的应用之一是电子印刷电路板 &#xff08;PCB&#xff09; 的检测和分析。本文…