2025年3月26日 星期三 甲辰(龙)年 月廿五 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > .net

[C#]Attribute + TypeConverter 实现 Excel To Json

时间:11-27来源:作者:点击数:122

json

起因

最近在项目上需要实现一个功能,把 Excel 的内容转成 Json 作为配置文件,对于 Excel 的操作,有个开源的类库 Epplus,而对于 Json 序列化,用到了 Newtonsoft 的 Json.NET,实现上使用了 Attribute + TypeConverter,Attribute 用于标记列与属性的对应关系,TypeConverter 用于处理特殊数据结构。

先看下效果:

Excel 数据
source

转换后Json
json

可以看到 C 列 含有空格,转换后变成了下划线,D 列被转换成了 List<string>

调用:

  • static void Main(string[] args)
  • {
  • using (var fileStream = File.Open("../../test.xlsx", FileMode.Open, FileAccess.Read))
  • {
  • xlPackage = new ExcelPackage(fileStream);
  • var workBook = xlPackage.Workbook;
  • var workSheet = workBook.Worksheets["sheet1"];
  • var converter = new ModelConverter<TestModel>(workSheet, 3);
  • var result = converter.Convert();
  • var json = JsonConvert.SerializeObject(result);
  • }
  • }

是不是很简洁呀

Epplus

基本介绍

Epplus 是用 .net 实现的用于对 Excel 进行读写操作的开源类库,封装了对 Excel 表格的读写操作,功能强大,还能生成公式。由于我们不负责维护 Excel ,只是从里面读取内容并转换成 Json,因此只需要用到 Epplus 提供的读取 Excel 的功能就够用了。Epplus 提供了如下功能:

  • 单元格范围读取
  • 单元格样式(边框,字体颜色,填充颜色,字体,数字格式,对齐样式)
  • 数据验证
  • 带条件格式化
  • 图表
  • 插入图片
  • 插入形状
  • Comments
  • 表格
  • 数据透视表
  • 文件保护
  • 加密
  • VBA 脚本
  • 公式计算等

安装

直接使用 Nuget 命令安装引用

  • Install-Package EPPlus -Version 4.5.2.1

注意

Epplus 基于 GNU License,如果直接修改和使用源码,由于 GNU License 的传染性,使得你的项目必须以相同的 License 进行授权,即必须开放源代码,所以使用源码要慎重,最好通过 Nuget 命令使用编译好的类库文件(dll),而不要直接使用源代码

Json.NET

Json.NET 是 Newtonsoft 提供的一个强大的处理 Json 文本的 .net 类库,实现了 Json 序列化,反序列化,按照 Json 路径访问,XML Json 互转等功能,这里我们只用到了 Json 序列化。

安装

直接使用 Nuget 命令安装

  • Install-Package Newtonsoft.Json -Version 11.0.2

实现思路

要实现 Excel to Json,大体分为四个步骤:

  1. 找到 Excel 表中每一列与 object 属性的对应关系
  2. 遍历 Excel 表中的每一行,转换成 object 集合
  3. 对于单元格中的数据,可能需要做特殊处理,比如读取单元格并替换内容里的空格,把单元格内容按一定格式转换成列表等
  4. 把生成的 object 集合转换成 J

本着方便扩展、解耦的原则,想到了一个 Attribute + TypeConverter 的实现,利用 Attribute 标记属性与 Excel 表格每列的对应关系,方便统一集中管理。用 TypeConvertor 能够使用 .net 自带的 TypeConverter Attribute 对需要特殊处理的字段进行自动格式转换。

Attribute

关于 Attribute 的相关知识请参考 C#系列之Attribute与反射

TypeConverter

TypeConverter 是 .net 提供的用于类型转换的基类,通过 override CanConvertFrom CanConvertToConvertFrom ConvertTo 方法来实现从特定类型转换到该类型,或者通过该类型转换成特定类型,在这里因为我们不需要把属性类型转换成其他类型,只需要把从 Excel 里面来的数据,通常是 string,转换成 object 属性声明的类型即可,因此只需要实现 CanConvertFrom 和 ConvertFrom。另外它需要一个 TypeConverterAttribute 配合一起使用,标记当前属性使用什么样的 TypeConverter 可以转换成该属性的声明类型,比如:

  • public class TestModel
  • {
  • ...
  • [WorkSheetColumn("D")]
  • [TypeConverter(typeof(ListStringConverter))]
  • public List Hobby { get; set; }
  • ...
  • }
  • public class ListStringConverter : TypeConverter
  • {
  • public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
  • {
  • if (sourceType == typeof(string))
  • {
  • return true;
  • }
  • return base.CanConvertFrom(context, sourceType);
  • }
  • public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
  • {
  • if (value is string)
  • {
  • var strValue = value.ToString();
  • if (string.IsNullOrEmpty(strValue) || strValue == "N/A")
  • {
  • return new List();
  • }
  • return value.ToString().Split(',').ToList();
  • }
  • return value;
  • }
  • }

标记对应关系

为了标记 object 属性和 Excel 单元格的对应关系,我们需要实现一个 WorksheetColumnAttribute, 它只包含一个属性 ColumnName, 限定用于 Property,为什么限定为 Property,下面说

  • [AttributeUsage(AttributeTargets.Property)]
  • public class WorkSheetColumnAttribute : Attribute
  • {
  • public string ColumnName { get; private set; }
  • public WorkSheetColumnAttribute(string columnName)
  • {
  • ColumnName = columnName;
  • }
  • }

Model 实现

Model 保存了所有的对应关系及转换关系,方便集中管理

  • public class TestModel
  • {
  • [WorkSheetColumn("A")]
  • public string Name { get; set; }
  • [WorkSheetColumn("B")]
  • public int Age { get; set; }
  • [WorkSheetColumn("C")]
  • [TypeConverter(typeof(NoSpaceConverter))]
  • public string FavoriteFruit { get; set; }
  • [WorkSheetColumn("D")]
  • [TypeConverter(typeof(ListStringConverter))]
  • public List Hobby { get; set; }
  • }

转化

转化分为2步

  1. 把 Model 上所有的 WorkSheetColumn 和 TypeConverter Attribute 找出,建立 列名-属性-TypeConverter 的对应关系,定义了一个叫做 MappingInfo 的类来存储对应关系。由于每一行的数据结构都一致,所以只需要建立一次对应关系就可以了。上面提到 WorkSheetColumnAttribute 限定只能应用到 Property,是因为我们在使用反射的时候直接遍历 Model 上的所有 Property,并保存成 PropertyInfo,这样就不用考虑 field 的情况,能够简化实现
  2. 循环遍历 Excel 的所有行,调用对应关系进行转换
  • public class ModelConverter<T> where T : class, new()
  • {
  • private readonly ExcelRange _excelRange;
  • private readonly int _startRow;
  • private readonly int _endRow;
  • public ModelConverter(ExcelWorksheet workSheet, int startRow)
  • {
  • _excelRange = workSheet.Cells;
  • _startRow = startRow;
  • _endRow = workSheet.Dimension.End.Row;
  • }
  • public IList Convert()
  • {
  • var mappingDic = GetMappingDic();
  • var result = new List<T>();
  • for (var index = _startRow; index <= _endRow; index++)
  • {
  • var instance = new T();
  • foreach (var mappingInfo in mappingDic)
  • {
  • mappingInfo.PropertyInfo.SetValue(instance,
  • mappingInfo.TypeConverter.ConvertFrom(_excelRange[string.Format("{0}{1}", mappingInfo.ColumnName, index)].Text), (object[])null);
  • }
  • result.Add(instance);
  • }
  • return result;
  • }
  • private List<MappingInfo> GetMappingDic()
  • {
  • var properties = typeof(T).GetProperties();
  • var result = new List<MappingInfo>();
  • foreach (var propertyInfo in properties)
  • {
  • var workColumnAttribute =(WorkSheetColumnAttribute)propertyInfo.GetCustomAttributes(typeof(WorkSheetColumnAttribute), false).FirstOrDefault();
  • if (workColumnAttribute == null)
  • {
  • continue;
  • }
  • var mappingInfo = new MappingInfo()
  • {
  • ColumnName = workColumnAttribute.ColumnName,
  • PropertyInfo = propertyInfo,
  • };
  • var propertyDescriptorCollection = TypeDescriptor.GetProperties(typeof(T));
  • mappingInfo.TypeConverter = propertyDescriptorCollection.Find(propertyInfo.Name, false).Converter;
  • result.Add(mappingInfo);
  • }
  • return result;
  • }
  • public class MappingInfo
  • {
  • public PropertyInfo PropertyInfo { get; set; }
  • public string ColumnName { get; set; }
  • public TypeConverter TypeConverter { get; set; }
  • }
  • }

Json 序列化

序列化用到 Json.NET 的 JsonConvert 类,一行代码搞定

  • var json = JsonConvert.SerializeObject(result);

优缺点

优点:

  • 把属性与列名对应关系集中在一起,方便维护,并且易于扩展,加入新的列只需要新加属性就可以了
  • 使用 TypeConveterAttribute 进行特殊处理,对于新的类型处理只需新加 TypeConverter 就可以,不用修改 ModelConverter 类,做到了开闭有度
  • 只创建一次对应关系
  • 用这种思路可以实现其他 class 到 class 的类型转换

缺点:

  • 只实现了简单的 Excel 列对应关系,对于复杂的表格,还需要进一步考虑对应关系的设计,如好几列共同构成一个子类型

完整代码

见 Github

参考链接

TypeConverterAttribute
TypeConverter

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门