将Excel数据转换成FlatBuffer数据的方法
来源:互联网 发布:淘宝网络科技有限公司 编辑:程序博客网 时间:2024/06/02 01:14
之前配置表中用的Excel,然后转成ProtoBuffer,这个格式虽然跨平台,但限制也不少,而且对U3D不友好,所以考虑是否有其他替代方案。网上搜了之后发现FlatBuffer不错,所以就想把配置表的数据转换成FlatBuffer。思路和实现步骤如下:
1. 读取Excel
目前比较流行的是NPOI(以前用OLEDB,但Eexcel升级到365之后就不好用了!),所以最好是使用NPOI读取,代码如下:
private static void ReadExcelNPOI() { string[] files = Directory.GetFiles(_excelPath, "*.xlsx"); foreach (string file in files) { string excelName = Path.GetFileNameWithoutExtension(file); // 正在编辑的Excel文件会生成一个临时文件,并以~$开头 if (excelName.StartsWith("~$")) continue; XSSFWorkbook wk = new XSSFWorkbook(file); int count = wk.Count; for(int n = 0; n < count; ++n) { ISheet sheet = wk[n]; string sheetName = sheet.SheetName; // 忽略描述页 if (sheetName == "description") continue; if (!sheetName.EndsWith("Config")) { Console.WriteLine(string.Format("\n{0}.xlsx中的{1}标签必须以Config结尾!", excelName, sheetName)); continue; } ExcelData data = new ExcelData(); data.excelName = excelName; data.sheetName = sheetName;#if USE_NPOI data.sheet = sheet;#endif _excelDatas.Add(data); } } }
2. 根据数据表信息生成FlatBuffer使用的fbs文件(用来生成不同语言的代码)
这一步的思路是每个表对应一个fbs文件,先根据表信息生成对应的fbs数据结构,再添加一个该数据结构的数组表示整个数据表。代码如下:
private void GenFBS() { Console.WriteLine("\n----------生成FBS文件----------"); foreach (ExcelData data in _excels) { string fileName = AppConfigs.Instance.FBSPath + "/" + data.sheetName + ".fbs"; EnsureDirectory(AppConfigs.Instance.FBSPath); StringBuilder sb = new StringBuilder(); string dataClass = "Single" + data.sheetName + "Data"; sb.Append("namespace Config;" + NewLine); sb.Append(NewLine); sb.Append("table " + data.sheetName + "{" + NewLine); sb.Append(Tab + "data:[" + dataClass + "];" + NewLine); sb.Append("}" + NewLine); sb.Append(NewLine); sb.Append("table " + dataClass + "{" + NewLine); foreach (ExcelFieldInfo info in data.filedInfos.Values) { sb.Append(Tab + info.name + ":" + info.type + ";" + NewLine); } sb.Append("}" + NewLine); sb.Append(NewLine); sb.Append("root_type " + data.sheetName + ";" + NewLine); sb.Append("file_identifier \"WHAT\";"); FileStream fs = new FileStream(fileName, FileMode.Create); StreamWriter sw = new StreamWriter(fs, Encoding.UTF8); sw.Write(sb.ToString()); sw.Close(); fs.Close(); } }
3. 使用FlatBuffer生成对应的操作代码,并生成对应的保存数据的代码
这一步其实是两步,首先使用FlatBuffer将fbs文件转换成对应的操作代码,我这里用的是C#,然后再生成对应的保存数据到文件的代码,代码如下: private void GenCode() { Console.WriteLine("\n----------生成C#代码----------"); foreach (ExcelData data in _excels) { string fbsName = AppConfigs.Instance.FBSPath + "/" + data.sheetName + ".fbs"; EnsureDirectory(AppConfigs.Instance.GenerateCodePath); // 配置操作配置文件的代码 string arguments = string.Format("--csharp -o {0} {1} --gen-onefile", AppConfigs.Instance.GenerateCodePath, fbsName); ProcCmd(AppConfigs.Instance.FlatC, arguments, AppConfigs.Instance.FBSPath, false); // 生成将配置文件存储成文件的代码 string dataClass = "Single" + data.sheetName + "Data"; string className = data.sheetName + ConfigSaveFileEx; StringBuilder sb = new StringBuilder(); sb.Append("using System;" + NewLine); sb.Append("using System.IO;" + NewLine); sb.Append("using System.Collections.Generic;" + NewLine); sb.Append("using FlatBuffers;" + NewLine); sb.Append("using Config;" + NewLine); sb.Append(NewLine); sb.Append("public class " + className + " {" + NewLine); sb.Append(NewLine); sb.Append(Tab + "public class Data {" + NewLine); foreach (ExcelFieldInfo info in data.filedInfos.Values) { sb.Append(Tab + Tab + "public " + FieldToType(info.type) + " " + info.name + ";" + NewLine); } sb.Append(Tab + "}" + NewLine); sb.Append(NewLine); sb.Append(Tab + "public void Save(List datas, string path) {" + NewLine); sb.Append(Tab + Tab + "FlatBufferBuilder fbb = new FlatBufferBuilder(1);" + NewLine); sb.Append(Tab + Tab + "int count = datas.Count;" + NewLine); sb.Append(string.Format(Tab + Tab + "Offset<{0}>[] offsets = new Offset<{1}>[count];", dataClass, dataClass) + NewLine); sb.Append(Tab + Tab + "for (int n = 0; n < count; ++n) {" + NewLine); sb.Append(Tab + Tab + Tab + "Data data = datas[n];" + NewLine); sb.Append(string.Format(Tab + Tab + Tab + "offsets[n] = {0}.Create{1}(fbb,", dataClass, dataClass) + NewLine); int index = 0; foreach (ExcelFieldInfo info in data.filedInfos.Values) { string type = FieldToType(info.type); string end = ","; if (index == data.filedInfos.Count - 1) { end = ");"; } if (type == "string") { sb.Append(string.Format(Tab + Tab + Tab + "fbb.CreateString(data.{0})", info.name) + end + NewLine); } else { sb.Append(Tab + Tab + Tab + "data." + info.name + end + NewLine); } index++; } sb.Append(Tab + Tab + "}" + NewLine); sb.Append(Tab + Tab + string.Format("VectorOffset dataOff = {0}.CreateDataVector(fbb, offsets);", data.sheetName) + NewLine); sb.Append(string.Format(Tab + Tab + "var configOff = {0}.Create{1}(fbb, dataOff);", data.sheetName, data.sheetName) + NewLine); sb.Append(string.Format(Tab + Tab + "{0}.Finish{1}Buffer(fbb, configOff);", data.sheetName, data.sheetName) + NewLine); sb.Append(Tab + Tab + "using (var ms = new MemoryStream(fbb.DataBuffer.Data, fbb.DataBuffer.Position, fbb.Offset)) {" + NewLine); sb.Append(Tab + Tab + Tab + "File.WriteAllBytes(path, ms.ToArray());" + NewLine); sb.Append(Tab + Tab + "}" + NewLine); sb.Append(Tab + "}" + NewLine); sb.Append("}" + NewLine); string fileName = AppConfigs.Instance.GenerateCodePath + className + ".cs"; FileStream fs = new FileStream(fileName, FileMode.Create); StreamWriter sw = new StreamWriter(fs, Encoding.UTF8); sw.Write(sb.ToString()); sw.Close(); fs.Close(); } }
4. 动态编译生成的C#代码
这里不多说,直接上代码: private void CompileCSharp() { // 休息一会儿,避免上一步生成文件的操作还没有完成 Thread.Sleep(1000); Console.WriteLine("\n----------编译C#代码----------"); string[] files = Directory.GetFiles(AppConfigs.Instance.GenerateCodePath); List compileFiles = new List(); // 过滤文件 for (int n = 0; n < files.Length; ++n) { if (Path.GetExtension(files[n]) != ".cs") { continue; } compileFiles.Add(files[n]); } string flatRefPath = AppConfigs.Instance.CompileRefPath + "FlatBuffers.dll"; _flatAssmbly = CompileCS(flatRefPath, null, null, compileFiles.ToArray()); } private Assembly CompileCS(string refer, string output, string options, params string[] code) { CodeDomProvider domProvider = CodeDomProvider.CreateProvider("CSharp"); CompilerParameters compileParams = new CompilerParameters(); compileParams.GenerateExecutable = false; compileParams.GenerateInMemory = true; compileParams.ReferencedAssemblies.Add(AppConfigs.Instance.CompileRefPath + "System.dll"); if (!string.IsNullOrEmpty(refer)) { compileParams.ReferencedAssemblies.Add(refer); } if (!string.IsNullOrEmpty(output)) { compileParams.OutputAssembly = output; } if (!string.IsNullOrEmpty(options)) { compileParams.CompilerOptions = options; } CompilerResults compileResults = domProvider.CompileAssemblyFromFile(compileParams, code); if (compileResults.Errors.Count > 0) { Console.WriteLine("compile error!"); foreach (CompilerError error in compileResults.Errors) { Console.WriteLine(string.Format(" {0}", error.ToString())); Console.WriteLine(""); } } return compileResults.CompiledAssembly; }
5. 生成最终的bytes文件
用C#的反射动态生成对应的数据结构,然后将excel的数据给数据赋值,最终调用保存方法就成了,代码如下: private void GenDataFiles() { Console.WriteLine("\n----------生成数据文件----------"); foreach (ExcelData data in _excels) { string classTypeName = data.sheetName + ConfigSaveFileEx; // 反射生成类中类要用+连接 string dataTypeName = data.sheetName + ConfigSaveFileEx + "+Data"; object datas = Utils.CreateGeneric(typeof(List<>), _flatAssmbly.GetType(dataTypeName));#if USE_NPOI for (int n = 1; n <= data.sheet.LastRowNum; ++n) { IRow dataRow = data.sheet.GetRow(n); // 第一个字段为空,则认为整个数据表已经结束 if ("" == dataRow.GetCell(0).ToString().Trim()) { break; }#else for (int n = 1; n < data.table.Rows.Count; ++n) { DataRow dataRow = data.table.Rows[n]; // 第一个字段为空,则认为整个数据表已经结束 if ("" == dataRow[0].ToString().Trim()) { break; }#endif object dataInst = _flatAssmbly.CreateInstance(dataTypeName); System.Reflection.FieldInfo[] fields = dataInst.GetType().GetFields(); foreach (System.Reflection.FieldInfo pi in fields) { ExcelFieldInfo fieldInfo = null; string infoValue = null; object value = null; try { fieldInfo = data.filedInfos[pi.Name];#if USE_NPOI infoValue = dataRow.GetCell(fieldInfo.index).ToString();#else infoValue = dataRow[fieldInfo.index].ToString();#endif value = GetFieldValue(fieldInfo, infoValue, _flatAssmbly); pi.SetValue(dataInst, value); } catch (Exception e) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(string.Format("{0}, 行: {1}, 列: {2}", e.Message, n + 1, fieldInfo.desc)); Console.ResetColor(); } } datas.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, datas, new object[] { dataInst }); } object classInst = _flatAssmbly.CreateInstance(classTypeName); object[] parameters = new object[2]; parameters[0] = datas; parameters[1] = AppConfigs.Instance.ClientDataPath + data.sheetName + ".bytes"; Utils.CallMethod(classInst, "Save", parameters); } }
private void GenCode() { Console.WriteLine("\n----------生成C#代码----------"); foreach (ExcelData data in _excels) { string fbsName = AppConfigs.Instance.FBSPath + "/" + data.sheetName + ".fbs"; EnsureDirectory(AppConfigs.Instance.GenerateCodePath); // 配置操作配置文件的代码 string arguments = string.Format("--csharp -o {0} {1} --gen-onefile", AppConfigs.Instance.GenerateCodePath, fbsName); ProcCmd(AppConfigs.Instance.FlatC, arguments, AppConfigs.Instance.FBSPath, false); // 生成将配置文件存储成文件的代码 string dataClass = "Single" + data.sheetName + "Data"; string className = data.sheetName + ConfigSaveFileEx; StringBuilder sb = new StringBuilder(); sb.Append("using System;" + NewLine); sb.Append("using System.IO;" + NewLine); sb.Append("using System.Collections.Generic;" + NewLine); sb.Append("using FlatBuffers;" + NewLine); sb.Append("using Config;" + NewLine); sb.Append(NewLine); sb.Append("public class " + className + " {" + NewLine); sb.Append(NewLine); sb.Append(Tab + "public class Data {" + NewLine); foreach (ExcelFieldInfo info in data.filedInfos.Values) { sb.Append(Tab + Tab + "public " + FieldToType(info.type) + " " + info.name + ";" + NewLine); } sb.Append(Tab + "}" + NewLine); sb.Append(NewLine); sb.Append(Tab + "public void Save(List datas, string path) {" + NewLine); sb.Append(Tab + Tab + "FlatBufferBuilder fbb = new FlatBufferBuilder(1);" + NewLine); sb.Append(Tab + Tab + "int count = datas.Count;" + NewLine); sb.Append(string.Format(Tab + Tab + "Offset<{0}>[] offsets = new Offset<{1}>[count];", dataClass, dataClass) + NewLine); sb.Append(Tab + Tab + "for (int n = 0; n < count; ++n) {" + NewLine); sb.Append(Tab + Tab + Tab + "Data data = datas[n];" + NewLine); sb.Append(string.Format(Tab + Tab + Tab + "offsets[n] = {0}.Create{1}(fbb,", dataClass, dataClass) + NewLine); int index = 0; foreach (ExcelFieldInfo info in data.filedInfos.Values) { string type = FieldToType(info.type); string end = ","; if (index == data.filedInfos.Count - 1) { end = ");"; } if (type == "string") { sb.Append(string.Format(Tab + Tab + Tab + "fbb.CreateString(data.{0})", info.name) + end + NewLine); } else { sb.Append(Tab + Tab + Tab + "data." + info.name + end + NewLine); } index++; } sb.Append(Tab + Tab + "}" + NewLine); sb.Append(Tab + Tab + string.Format("VectorOffset dataOff = {0}.CreateDataVector(fbb, offsets);", data.sheetName) + NewLine); sb.Append(string.Format(Tab + Tab + "var configOff = {0}.Create{1}(fbb, dataOff);", data.sheetName, data.sheetName) + NewLine); sb.Append(string.Format(Tab + Tab + "{0}.Finish{1}Buffer(fbb, configOff);", data.sheetName, data.sheetName) + NewLine); sb.Append(Tab + Tab + "using (var ms = new MemoryStream(fbb.DataBuffer.Data, fbb.DataBuffer.Position, fbb.Offset)) {" + NewLine); sb.Append(Tab + Tab + Tab + "File.WriteAllBytes(path, ms.ToArray());" + NewLine); sb.Append(Tab + Tab + "}" + NewLine); sb.Append(Tab + "}" + NewLine); sb.Append("}" + NewLine); string fileName = AppConfigs.Instance.GenerateCodePath + className + ".cs"; FileStream fs = new FileStream(fileName, FileMode.Create); StreamWriter sw = new StreamWriter(fs, Encoding.UTF8); sw.Write(sb.ToString()); sw.Close(); fs.Close(); } }
4. 动态编译生成的C#代码
这里不多说,直接上代码:
private void CompileCSharp() { // 休息一会儿,避免上一步生成文件的操作还没有完成 Thread.Sleep(1000); Console.WriteLine("\n----------编译C#代码----------"); string[] files = Directory.GetFiles(AppConfigs.Instance.GenerateCodePath); ListcompileFiles = new List (); // 过滤文件 for (int n = 0; n < files.Length; ++n) { if (Path.GetExtension(files[n]) != ".cs") { continue; } compileFiles.Add(files[n]); } string flatRefPath = AppConfigs.Instance.CompileRefPath + "FlatBuffers.dll"; _flatAssmbly = CompileCS(flatRefPath, null, null, compileFiles.ToArray()); } private Assembly CompileCS(string refer, string output, string options, params string[] code) { CodeDomProvider domProvider = CodeDomProvider.CreateProvider("CSharp"); CompilerParameters compileParams = new CompilerParameters(); compileParams.GenerateExecutable = false; compileParams.GenerateInMemory = true; compileParams.ReferencedAssemblies.Add(AppConfigs.Instance.CompileRefPath + "System.dll"); if (!string.IsNullOrEmpty(refer)) { compileParams.ReferencedAssemblies.Add(refer); } if (!string.IsNullOrEmpty(output)) { compileParams.OutputAssembly = output; } if (!string.IsNullOrEmpty(options)) { compileParams.CompilerOptions = options; } CompilerResults compileResults = domProvider.CompileAssemblyFromFile(compileParams, code); if (compileResults.Errors.Count > 0) { Console.WriteLine("compile error!"); foreach (CompilerError error in compileResults.Errors) { Console.WriteLine(string.Format(" {0}", error.ToString())); Console.WriteLine(""); } } return compileResults.CompiledAssembly; }
5. 生成最终的bytes文件
用C#的反射动态生成对应的数据结构,然后将excel的数据给数据赋值,最终调用保存方法就成了,代码如下:
private void GenDataFiles() { Console.WriteLine("\n----------生成数据文件----------"); foreach (ExcelData data in _excels) { string classTypeName = data.sheetName + ConfigSaveFileEx; // 反射生成类中类要用+连接 string dataTypeName = data.sheetName + ConfigSaveFileEx + "+Data"; object datas = Utils.CreateGeneric(typeof(List<>), _flatAssmbly.GetType(dataTypeName));#if USE_NPOI for (int n = 1; n <= data.sheet.LastRowNum; ++n) { IRow dataRow = data.sheet.GetRow(n); // 第一个字段为空,则认为整个数据表已经结束 if ("" == dataRow.GetCell(0).ToString().Trim()) { break; }#else for (int n = 1; n < data.table.Rows.Count; ++n) { DataRow dataRow = data.table.Rows[n]; // 第一个字段为空,则认为整个数据表已经结束 if ("" == dataRow[0].ToString().Trim()) { break; }#endif object dataInst = _flatAssmbly.CreateInstance(dataTypeName); System.Reflection.FieldInfo[] fields = dataInst.GetType().GetFields(); foreach (System.Reflection.FieldInfo pi in fields) { ExcelFieldInfo fieldInfo = null; string infoValue = null; object value = null; try { fieldInfo = data.filedInfos[pi.Name];#if USE_NPOI infoValue = dataRow.GetCell(fieldInfo.index).ToString();#else infoValue = dataRow[fieldInfo.index].ToString();#endif value = GetFieldValue(fieldInfo, infoValue, _flatAssmbly); pi.SetValue(dataInst, value); } catch (Exception e) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(string.Format("{0}, 行: {1}, 列: {2}", e.Message, n + 1, fieldInfo.desc)); Console.ResetColor(); } } datas.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, datas, new object[] { dataInst }); } object classInst = _flatAssmbly.CreateInstance(classTypeName); object[] parameters = new object[2]; parameters[0] = datas; parameters[1] = AppConfigs.Instance.ClientDataPath + data.sheetName + ".bytes"; Utils.CallMethod(classInst, "Save", parameters); } }
阅读全文
0 0
- 将Excel数据转换成FlatBuffer数据的方法
- asp将数据库的数据转换成excel导出
- 技巧: 将 Excel 数据转换成 XML
- JavaScript将数据转换成整数的方法
- JavaScript将数据转换成整数的方法
- C# 将二进制数据转换成BASE64字符串的方法
- 将pdf转换成excel的方法
- 将数据集转换为Excel格式的一个实现
- 将指定Excel文件中的数据转换成DataTable对象
- java 将EXCEL表格数据转换成XML格式
- java 将EXCEL表格数据转换成XML格式
- java 将EXCEL表格数据转换成XML格式
- java将excel单元格数据转换成sql语句
- 将Excel中的数据转换成sql Insert语句
- 将Excel中的数据转换成sql Insert语句
- 将Excel中的数据转换成sql Insert语句
- 将Excel中的数据转换成sql Insert语句
- 将DataGridView中的表数据转换Excel
- Android 7.0获取文件Uri适配
- Android8.0 Email 一个Bug修改方案
- Linux tcpdump命令详解
- javascript:;与javascript:void(0)使用介绍
- jquery表单过滤选择器
- 将Excel数据转换成FlatBuffer数据的方法
- Codeforces Round #443 (Div. 2) E. Tournament
- 反射(提供封装程序集、模块和类型的对象)
- Syabse数据库无法启动的数据恢复案例
- 【Owin 学习系列】2. Owin Startup 类解析
- Redis学习笔记之四:列表类型
- Map转换成Bean对象
- 坐在马桶上看算法:快速排序
- Delphi压缩access数据库