复杂Excel转换与导入
需求
把不同客户提供Excel 直接导入到系统中生成对应的收货单或是出货单。后端创建收货端和出货单的接口已经有现成的webservice或是标准的xml;这类需要做的就是把客户提供不同种类的Excel mapping成标准格式。
要重点解决问题
不同格式的excel如何找到对应的数据项,比如一个Excel中需要字段分别在不同的sheet或是不同的位置上。
解决方案
第一个定位配置信息,sheet-name:数据从哪个sheet中读取,默认sheet1,start-tag:固定标识,查找Excel中一些特殊文本信息来定位具体的celladdress(行,列),data-offset:设置一个偏移量,在找到具体内容的地址后可能真正需要数据在后面,那就需要设置一个偏移量待读取信息, end-tag:结束位置,用于循环读取的范围。
第二个映射字段明,XmlNode Name:就是目标字段明,data-field:Excel中对应的字段名称(含有表头的行),data-type:目标字段的类型,data-formatter:格式化截取excel Cell中的内容 比如:需要通过substring,或splitl来取其中的内容。
第三个表示单个表头,还是循环的表体 replicate="true" 表示需要循环读取。
大致的处理过程

先根据配置规则把Excel中需要的信息提取出来并生成一个XML文档,如果标准的接口XML结构和数据都比较复杂,那么还需要使用XSLT语言来做更复杂的mapping,满足后端服务的要求。
实际运行的过程

原始Excel

配置规则XML

转换成初步XML
实现的代码
代码非常的简单,还是第一个版本,以后再慢慢优化和重构
class Program
{
static async Task Main(string[] args)
{
var path = @"d:\9C箱单0000880680.xlsx";
var configpath = @"d:\XslImportRule1.xml";
var xdoc = XDocument.Load(configpath);
var root = xdoc.Root.Name;
var descxml = new XDocument();
descxml.Add(new XElement(xdoc.Root.Name));
var workbook = new XSSFWorkbook(path);
Process(workbook,null, xdoc.Root, , descxml.Root,null);
descxml.Save("d:\\output.xml");
return;
}
static void Process(IWorkbook book, ISheet sheet, XElement element, int depth, XElement root,DataRow dr)
{
var pelment = element.Parent;
var name = element.Name;
var atts = element.Attributes();
var replicate = atts.Where(x => x.Name == "replicate").FirstOrDefault()?.Value;
var sheetname = atts.Where(x => x.Name == "sheet-name").FirstOrDefault()?.Value;
var starttag = atts.Where(x => x.Name == "start-tag").FirstOrDefault()?.Value;
var start = atts.Where(x => x.Name == "start").FirstOrDefault()?.Value;
var endtag = atts.Where(x => x.Name == "end-tag").FirstOrDefault()?.Value;
var end = atts.Where(x => x.Name == "end").FirstOrDefault()?.Value;
var fieldname = atts.Where(x => x.Name == "data-field").Select(x => x.Value).FirstOrDefault();
var datatype = atts.Where(x => x.Name == "data-type").Select(x => x.Value);
var defaultvalue = atts.Where(x => x.Name == "data-default").FirstOrDefault()?.Value;
var formatter = atts.Where(x => x.Name == "data-formatter").FirstOrDefault()?.Value;
var offset = atts.Where(x => x.Name == "data-offset").FirstOrDefault()?.Value;
XElement copyelement = null; //if (element.Parent != null )
//{
// copyelement = new XElement(name);
// root.Add(copyelement);
//}
if (!string.IsNullOrEmpty(replicate) && !string.IsNullOrEmpty(sheetname)) {
sheet = book.GetSheet(sheetname);
} if (!element.HasElements)
{
copyelement = new XElement(name);
root.Add(copyelement);
// element is child with no descendants
if (dr == null)
{
CellAddress celladdress = null;
if (!string.IsNullOrEmpty(starttag))
{
celladdress = findXslx(sheet, starttag);
}
else if (!string.IsNullOrEmpty(start))
{
celladdress = new CellAddress(new CellReference(start));
}
if (celladdress != null)
{
var r = ;
var c = ;
if (!string.IsNullOrEmpty(offset))
{
var sp = offset.Split(';');
foreach (var ts in sp)
{
var sparray = ts.Split(':');
if (sparray[].Equals("c", StringComparison.OrdinalIgnoreCase))
{
c = Convert.ToInt32(sparray[]);
}
else
{
r = Convert.ToInt32(sparray[]);
}
}
}
var cell = sheet.GetRow(celladdress.Row + r).GetCell(celladdress.Column + c);
var val = getCellValue(cell);
if (string.IsNullOrEmpty(val) && !string.IsNullOrEmpty(defaultvalue))
{
val = defaultvalue;
}
if (!string.IsNullOrEmpty(val) && !string.IsNullOrEmpty(formatter))
{
var codescript = formatter.Replace("$", "\"" + val + "\"");
var fval = CSharpScript.EvaluateAsync<string>(codescript).Result;
val = fval;
}
copyelement.SetValue(val);
}
else if (!string.IsNullOrEmpty(defaultvalue))
{
copyelement.SetValue(defaultvalue);
}
}
else
{
if(dr.Table.Columns.Contains(fieldname))
{
var val = dr[fieldname].ToString();
if (string.IsNullOrEmpty(val) && !string.IsNullOrEmpty(defaultvalue))
{
val = defaultvalue; }
copyelement.SetValue(val);
}
else if(!string.IsNullOrEmpty(defaultvalue))
{
copyelement.SetValue(defaultvalue);
}
} }
else
{
depth++;
if (replicate == "true")
{
var datatable= filldatatable(sheet, starttag, start, endtag, end, offset);
if (datatable.Rows.Count > )
{
foreach (DataRow datarow in datatable.Rows)
{
copyelement = new XElement(name);
foreach (var child in element.Elements())
{
if (copyelement != null)
{
Process(book, sheet, child, depth, copyelement, datarow);
}
else
{
Process(book, sheet, child, depth, root, datarow);
} }
root.Add(copyelement);
}
}
}
else
{
if (element.Parent != null)
{
copyelement = new XElement(name);
root.Add(copyelement);
}
foreach (var child in element.Elements())
{
if (copyelement != null)
{
Process(book,sheet, child, depth, copyelement,null);
}
else
{
Process(book,sheet, child, depth, root,null);
} }
} depth--;
}
} private static DataTable filldatatable(ISheet sheet, string starttag, string start, string endtag, string end, string offset)
{
CellAddress startaddress = null;
CellAddress endaddress = null;
if (!string.IsNullOrEmpty(starttag))
{
startaddress = findXslx(sheet, starttag);
}
else if (!string.IsNullOrEmpty(start))
{
startaddress = new CellAddress(new CellReference(start));
}
else
{
startaddress = new CellAddress(new CellReference("A0"));
}
if (!string.IsNullOrEmpty(endtag))
{
endaddress = findXslx(sheet, endtag);
}
else if (!string.IsNullOrEmpty(end))
{
endaddress = new CellAddress(new CellReference(end));
}
else
{
endaddress = null;
}
var offsetr = ;
var offsetc = ;
if (!string.IsNullOrEmpty(offset))
{
var sp = offset.Split(';');
foreach (var ts in sp)
{
var sparray = ts.Split(':');
if (sparray[].Equals("c", StringComparison.OrdinalIgnoreCase))
{
offsetc = Convert.ToInt32(sparray[]);
}
else
{
offsetr = Convert.ToInt32(sparray[]);
}
}
}
var firstrow = startaddress == null ? sheet.FirstRowNum : startaddress.Row + offsetr;
var lastrow = (endaddress == null) ? sheet.LastRowNum : endaddress.Row;
var table = new DataTable();
var lastcell = ; //row.LastCellNum;
var firstcell = ; //row.FirstCellNum + offsetc;
for (int r = firstrow; r < lastrow; r++)
{
var row = sheet.GetRow(r);
if (row == null) continue; if (r == firstrow)
{
lastcell = row.LastCellNum;
firstcell = row.FirstCellNum + offsetc;
for (int c = firstcell; c < lastcell; c++)
{
var cell = row.GetCell(c);
if (cell == null) continue;
var strval = getCellValue(cell).Trim();
if (!string.IsNullOrEmpty(strval))
{
table.Columns.Add(new DataColumn(strval));
}
}
}
else
{
var dataRow = table.NewRow();
var array = new string[table.Columns.Count];
//for (var c = 0; c < table.Columns.Count; c++)
//{
// var cell = row.GetCell(firstcell+c);
// var val = getCellValue(cell).Trim();
// array[c] = val;
//}
for (int c = firstcell; c < lastcell; c++)
{
var cell = row.GetCell(c);
var val = getCellValue(cell).Trim();
array[c- firstcell] = val;
}
dataRow.ItemArray = array;
table.Rows.Add(dataRow);
}
}
return table;
} private static CellAddress findXslx(ISheet sheet, string key)
{
var lastrow = sheet.LastRowNum;
var firstrow = sheet.FirstRowNum;
for (int r = firstrow; r < lastrow; r++)
{
var row = sheet.GetRow(r);
if (row == null) continue;
var lastcell = row.LastCellNum;
var firstcell = row.FirstCellNum;
for (int c = firstcell; c < lastcell; c++)
{
var cell = row.GetCell(c);
if (cell == null) continue;
var strval = getCellValue(cell).Trim();
//if (strval.Trim().Equals(key, StringComparison.OrdinalIgnoreCase))
//{
// return cell.Address;
//}
if (match(key, strval))
{
return cell.Address;
}
}
}
return null;
}
private static string getCellValue(ICell cell)
{
if (cell == null)
{
return string.Empty;
}
var dataFormatter = new DataFormatter(CultureInfo.CurrentCulture); // If this is not part of a merge cell,
// just get this cell's value like normal.
if (!cell.IsMergedCell)
{
return dataFormatter.FormatCellValue(cell);
} // Otherwise, we need to find the value of this merged cell.
else
{
// Get current sheet.
var currentSheet = cell.Sheet; // Loop through all merge regions in this sheet.
for (int i = ; i < currentSheet.NumMergedRegions; i++)
{
var mergeRegion = currentSheet.GetMergedRegion(i); // If this merged region contains this cell.
if (mergeRegion.FirstRow <= cell.RowIndex && cell.RowIndex <= mergeRegion.LastRow &&
mergeRegion.FirstColumn <= cell.ColumnIndex && cell.ColumnIndex <= mergeRegion.LastColumn)
{
// Find the top-most and left-most cell in this region.
var firstRegionCell = currentSheet.GetRow(mergeRegion.FirstRow)
.GetCell(mergeRegion.FirstColumn); // And return its value.
return dataFormatter.FormatCellValue(firstRegionCell);
}
}
// This should never happen.
throw new Exception("Cannot find this cell in any merged region");
}
} static bool match(string pattern, string input)
{
if (String.Compare(pattern, input) == )
{
return true;
}
else if (String.IsNullOrEmpty(input))
{
if (String.IsNullOrEmpty(pattern.Trim(new Char[] { '*' })))
{
return true;
}
else
{
return false;
}
}
else if (pattern.Length == )
{
return false;
}
else if (pattern[] == '?')
{
return match(pattern.Substring(), input.Substring());
}
else if (pattern[pattern.Length - ] == '?')
{
return match(pattern.Substring(, pattern.Length - ),
input.Substring(, input.Length - ));
}
else if (pattern[] == '*')
{
if (match(pattern.Substring(), input))
{
return true;
}
else
{
return match(pattern, input.Substring());
}
}
else if (pattern[pattern.Length - ] == '*')
{
if (match(pattern.Substring(, pattern.Length - ), input))
{
return true;
}
else
{
return match(pattern, input.Substring(, input.Length - ));
}
}
else if (pattern[] == input[])
{
return match(pattern.Substring(), input.Substring());
}
return false;
}
}
}
代码库
https://github.com/neozhu/excelcompleximport
最近还会继续更新
复杂Excel转换与导入的更多相关文章
- piap.excel 微软 时间戳转换mssql sql server文件时间戳转换unix 导入mysql
piap.excel 微软 时间戳转换mssql sql server文件时间戳转换unix 导入mysql 需要不个mssql的sql文件导入mysql.他们的时间戳格式不同..ms用的是自定义的时 ...
- SQL SERVER 与ACCESS、EXCEL的数据导入导出转换
* 说明:复制表(只复制结构,源表名:a 新表名:b) select * into b from a where 1<>1 * 说明:拷贝表(拷贝数据,源表名:a 目标表名:b) ...
- TestLink学习七:TestLink测试用例Excel转换XML工具
TestLink对于测试用例的管理来说,是蛮强大的,但是在导入导出这块,功能有点弱,本文针对测试用例的导入,转载了一个Excel转换成xml工具. 1.根据到处的测试用例xml,定义一下我的Excel ...
- 将Excel中数据导入数据库(三)
上篇文章将Excel中数据导入数据库时,将从Excel读入的数据均转换成了数据库相应字段的类型,其实这是没有必要的,因为对于数据库各种类型的插入,均可以字符串格式插入.比如表WQ_SWMSAR_A字段 ...
- 将Excel中数据导入数据库(二)
在上篇文章中介绍到将Excel中数据导入到数据库中,但上篇文章例子只出现了nvachar类型,且数据量很小.今天碰到将Excel中数据导入数据库中的Excel有6419行,其中每行均有48个字段,有i ...
- 转:TestLink1.9.3测试用例:Excel转换XML工具<二>实现代码
TestLink1.9.3测试用例:Excel转换XML工具<二>实现代码 http://blog.csdn.net/candle806/article/details/7490599 以 ...
- 转:Excel转换XML工具<一>
http://blog.csdn.net/candle806/article/details/7441695最近在整理测试用例,所以想找一个合适的工具来完成对测试需求.测试用例的管理.对比了一翻,发现 ...
- 把Excel的数据导入到数据库
将Excel作为数据源,将数据导入数据库,是SSIS的一个简单的应用,下图是示例Excel,数据列是code和name 第一部分,Excel中的数据类型是数值类型 1,使用SSDT创建一个packag ...
- SQL SERVER 和ACCESS、EXCEL的数据导入导出
SQL SERVER 与ACCESS.EXCEL之间的数据转换SQL SERVER 和ACCESS的数据导入导出[日期:2007-05-06] 来源:Linux公社 作者:Linux 熟 悉 ...
随机推荐
- SpringMVC框架——转发与重定向
网上摘取一段大神总结的转发与重定向的区别,如下: 转发(服务端行为) 形式:request.getRequestDispatcher().forward(request,response) 转发在服务 ...
- 更新Linux服务器时间
1.修改系统时区(不修改的话,你同步时间会发现总是不对) ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime --这里我修改为了上海 2.安 ...
- 贵州省网络安全知识竞赛团体赛Writeup-phpweb部分
0x01 混淆后门#conn.php 首先还是拖到D盾扫描 打开conn.php发现底部有那么一串代码: 对这个代码进行分析 首先可以对几个比较简单的变量输出看一下 $s输出内容为create_fun ...
- 常见排序算法总结与分析之交换排序与插入排序-C#实现
前言 每每遇到关于排序算法的问题总是不能很好的解决,对一些概念,思想以及具体实现的认识也是模棱两可.归根结底,还是掌握不够熟练.以前只是看别人写,看了就忘.现在打算自己写,写些自己的东西,做个总结.本 ...
- 进制-Iterative-进制转换
2019-12-02 21:15:31 进制转换是计算机科学里的一个基础算法,通常可以使用如下的模版来进行计算. 下面我们来讨论一些关于进制的题目. 1271. Hexspeak 问题描述: 问题求 ...
- Ubuntu环境下部署Django+uwsgi+nginx总结
前言 这是我在搭建Django项目时候的过程,拿来总结记录,以备不时之需. 项目采用nginx+uwsgi的搭配方式. 项目依赖包采用requirements.txt文件管理的方式. 本地准备工作 确 ...
- 在dev分支上修改了文件,但是并没有执行git add. 和git commit命令,然后切换到master分支,仍然能看到dev分支的改动现象
当我们创建一个新的分支dev,并且在新分支上修改了原文件,在我们没有提交到仓库的前提下,将分支再切换到master分支上,执行git status ,可以看到dev操作的状态: (1)因为未add的内 ...
- 02 LED翻转与计数器使用
一. 设计定义: 计数器设计与验证 LED,每500ms,状态翻转一次也就是亮灭. 第一步: 系统时钟频率为50M,对应为T= =20ns 计数周期或者时间是500ms,计数次数的计算: 计数值=( ...
- TensorFlow系列专题(八):七步带你实现RNN循环神经网络小示例
欢迎大家关注我们的网站和系列教程:http://panchuang.net/ ,学习更多的机器学习.深度学习的知识! [前言]:在前面的内容里,我们已经学习了循环神经网络的基本结构和运算过程,这一小节 ...
- ES6全面讲解
写在之前.讲解了比较常用的ES6知识点,可以快速的入门.有两个比较复杂的知识点(promise,generator)则是留在了其他文章中,在里面详细讲解. 介绍 1.历史 1.ECMAScript ...