Attribute + TypeConverter 实现 Excel To Json

栏目: 后端 · 前端 · 发布时间: 6年前

内容简介:最近在项目上需要实现一个功能,把 Excel 的内容转成 Json 作为配置文件,对于 Excel 的操作,有个开源的类库先看下效果:可以看到 C 列 含有空格,转换后变成了下划线,D 列被转换成了 List<string>

起因

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

先看下效果:

Excel 数据

Attribute + TypeConverter 实现 Excel To Json

转换后Json

Attribute + TypeConverter 实现 Excel To 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 CanConvertTo ConvertFrom ConvertTo 方法来实现从特定类型转换到该类型,或者通过该类型转换成特定类型,在这里因为我们不需要把属性类型转换成其他类型,只需要把从 Excel 里面来的数据,通常是 string,转换成 object 属性声明的类型即可,因此只需要实现 CanConvertFromConvertFrom 。另外它需要一个 TypeConverterAttribute 配合一起使用,标记当前属性使用什么样的 TypeConverter 可以转换成该属性的声明类型,比如:

public class TestModel
{
...
    [WorkSheetColumn("D")]
    [TypeConverter(typeof(ListStringConverter))]
    public List
<string>
  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
 <string>
  ();
            }

            return value.ToString().Split(',').ToList();
        }

        return value;
    }
}
 </string>
</string>

标记对应关系

为了标记 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
<string>
  Hobby { get; set; }
}
</string>

转化

转化分为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
<t>
  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; }
	}
}

</t>

Json 序列化

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

var json = JsonConvert.SerializeObject(result);

优缺点

博主为你专属推荐

优点:

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

缺点:

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

完整代码

Github

参考链接

有问题请在评论区留言


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro HTML5 Programming

Pro HTML5 Programming

Peter Lubbers、Brian Albers、Frank Salim / Apress / 2010-9-1 / USD 49.99

HTML5 is here, and with it, web applications take on a power, ease, scalability, and responsiveness like never before. In this book, developers will learn how to use the latest cutting-edge HTML5 web ......一起来看看 《Pro HTML5 Programming》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具