原文地址:https://blog.csdn.net/qq_14903317/article/details/78637440
感谢原作者提供的思路,在原作者基础上做了一些完善,实现思路请移步原作者博客。
using System.Collections.Generic;
using System.Reflection;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System;
using UnityEngine;
using UnityEditor;
using Excel;
namespace WJExcelDataManager
{
public class ExcelDataTool
{
private static string INPUT_PATH;
public const string CODE_NAMESPACE = "WJExcelDataClass";//由表生成的数据类型均在此命名空间内
public const string BinDataFolder = "BinConfigData";//序列化的数据文件都会放在此文件夹内,此文件夹位于Resources文件夹下用于读取数据
private static List codeList;//存放所有生成的类的代码
private static Dictionary> dataDict;//存放所有数据表内的数据,key:类名 value:数据
[UnityEditor.MenuItem("开发工具/导表工具/导入全部数据")]
public static void LoadAllExcelData()
{
//if (!EditorUtility.DisplayDialog("注意!!!", "导表前要关闭打开的数据表,否则会失败,是否继续?", "继续", "取消")) return;
INPUT_PATH = PlayerPrefs.GetString(System.Environment.CurrentDirectory + "ExcelDataInputPath", "");
if (string.IsNullOrEmpty(INPUT_PATH))
{
ProgressBar.HideBarWithFailInfo("\n请先设置数据表路径!");
throw new Exception("请先设置数据表路径!");
}
string[] files = Directory.GetFiles(INPUT_PATH, "*.xls");
if (files == null || files.Length == 0)
{
EditorUtility.DisplayDialog("注意!!!", "\n暂无可以导入的数据表!", "确定");
EditorUtility.ClearProgressBar();
throw new Exception("暂无可以导入的数据表!");
}
if (codeList == null)
{
codeList = new List();
}
else
{
codeList.Clear();
}
if (dataDict == null)
{
dataDict = new Dictionary>();
}
else
{
dataDict.Clear();
}
float step = 1f;
foreach (string item in files)
{
ProgressBar.UpdataBar("正在加载 " + item, step / files.Length * 0.4f);
step++;
GetExcelData(item);
}
if (codeList.Count == 0)
{
EditorUtility.DisplayDialog("注意!!!", "\n暂无可以导入的数据表!", "确定");
EditorUtility.ClearProgressBar();
throw new Exception("暂无可以导入的数据表!");
}
//编译代码,生成包含所有数据表内数据类型的dll
Assembly assembly = CompileCode(codeList.ToArray());
//准备序列化数据
string BinDataPath = System.Environment.CurrentDirectory + "/Assets/Resources/" + BinDataFolder;//序列化后的数据存放路径
if (Directory.Exists(BinDataPath)) Directory.Delete(BinDataPath, true);//删除旧的数据文件
Directory.CreateDirectory(BinDataPath);
step = 1;
foreach (KeyValuePair> each in dataDict)
{
ProgressBar.UpdataBar("序列化数据: " + each.Key, step / dataDict.Count * 0.6f + 0.399f);//0.399是为了进度条在生成所有代码以前不会走完显示完成弹窗
step++;
//Assembly.CreateInstance 方法 (String) 使用区分大小写的搜索,从此程序集中查找指定的类型,然后使用系统激活器创建它的实例化对象
object container = assembly.CreateInstance(CODE_NAMESPACE + "." + each.Key);
Type temp = assembly.GetType(CODE_NAMESPACE + "." + each.Key + "Item");
//序列化数据
Serialize(container, temp, each.Value, BinDataPath);
}
ProgressBar.UpdataBar("创建数据管理类: DataManager", 0.999f);
ScriptGenerator.CreateDataManager(assembly);
ProgressBar.UpdataBar("\n导表成功!", 1);
AssetDatabase.Refresh();
Debug.Log("导表成功! ");
}
//数据表内每一格数据
class ConfigData
{
public string Type;//数据类型
public string Name;//字段名
public string Data;//数据值
}
private static void GetExcelData(string inputPath)
{
FileStream stream = null;
try
{
stream = File.Open(inputPath, FileMode.Open, FileAccess.Read);
}
catch
{
EditorUtility.DisplayDialog("注意!!!", "\n请关闭 " + inputPath + " 后再导表!", "确定");
EditorUtility.ClearProgressBar();
throw new Exception("请关闭 " + inputPath + " 后再导表!");
}
IExcelDataReader excelReader = null;
if (inputPath.EndsWith(".xls")) excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
else if (inputPath.EndsWith(".xlsx")) excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
if (!excelReader.IsValid)
{
ProgressBar.HideBarWithFailInfo("\n无法读取的文件: " + inputPath);
EditorUtility.ClearProgressBar();
throw new Exception("无法读取的文件: " + inputPath);
}
else
{
do
{
// sheet name
string className = excelReader.Name;
string[] types = null;//数据类型
string[] names = null;//字段名
List dataList = new List();
int index = 1;
//开始读取
while (excelReader.Read())
{
//这里读取的是每一行的数据
string[] datas = new string[excelReader.FieldCount];
for (int j = 0; j < excelReader.FieldCount; ++j)
{
datas[j] = excelReader.GetString(j);
}
//空行不处理
if (datas.Length == 0 || string.IsNullOrEmpty(datas[0]))
{
++index;
continue;
}
//第4行表示类型
if (index == 4) types = datas;
//第5行表示变量名
else if (index == 5) names = datas;
//后面的表示数据
else if (index > 5)
{
//把读取的数据和数据类型,名称保存起来,后面用来动态生成类
List configDataList = new List();
for (int j = 0; j < datas.Length; ++j)
{
ConfigData data = new ConfigData();
data.Type = types[j];
data.Name = names[j];
data.Data = datas[j];
if (string.IsNullOrEmpty(data.Type) || string.IsNullOrEmpty(data.Data)) continue;//空的数据不处理
configDataList.Add(data);
}
dataList.Add(configDataList.ToArray());
}
++index;
}
if (string.IsNullOrEmpty(className))
{
ProgressBar.HideBarWithFailInfo("\n空的类名(excel页签名), 路径: " + inputPath);
throw new Exception("空的类名(excel页签名), 路径: " + inputPath);
}
if (names != null && types != null)
{
//根据刚才的数据来生成C#脚本
ScriptGenerator generator = new ScriptGenerator(inputPath, className, names, types);
//所有生成的类的代码最终保存在这个链表中
codeList.Add(generator.Generate());
if (dataDict.ContainsKey(className))
{
ProgressBar.HideBarWithFailInfo("\n类名重复:" + className + " ,路径: " + inputPath);
throw new Exception("类名重复: " + className + " ,路径: " + inputPath);
}
else dataDict.Add(className, dataList);
}
}
while (excelReader.NextResult());//excelReader.NextResult() Excel表下一个sheet页有没有数据
}
stream.Dispose();
stream.Close();
}
//编译代码
private static Assembly CompileCode(string[] scripts)
{
string path = System.Environment.CurrentDirectory + "/Assets/Plugins/" + CODE_NAMESPACE;
if (Directory.Exists(path)) Directory.Delete(path, true);//删除旧dll
Directory.CreateDirectory(path);
//编译器实例对象
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
//编译器参数实例对象
CompilerParameters objCompilerParameters = new CompilerParameters();
objCompilerParameters.ReferencedAssemblies.AddRange(new string[] { "System.dll" });//添加程序集引用
objCompilerParameters.OutputAssembly = path + "/" + CODE_NAMESPACE + ".dll";//设置输出的程序集名
objCompilerParameters.GenerateExecutable = false;
objCompilerParameters.GenerateInMemory = true;
//开始编译脚本
CompilerResults cr = codeProvider.CompileAssemblyFromSource(objCompilerParameters, scripts);
if (cr.Errors.HasErrors)
{
ProgressBar.HideBarWithFailInfo("\n编译dll出错(详情见控制台)!");
foreach (CompilerError err in cr.Errors)
{
Debug.LogError(err.ErrorText);
}
throw new Exception("编译dll出错!");
}
Debug.Log("已生成 " + path + "/" + CODE_NAMESPACE + ".dll ");
return cr.CompiledAssembly;
}
//序列化对象
private static void Serialize(object container, Type temp, List dataList,string BinDataPath)
{
//设置数据
foreach (ConfigData[] datas in dataList)
{
//Type.FullName 获取该类型的完全限定名称,包括其命名空间,但不包括程序集。
object t = temp.Assembly.CreateInstance(temp.FullName);
foreach (ConfigData data in datas)
{
//Type.GetField(String) 搜索Type内指定名称的公共字段。
FieldInfo info = temp.GetField(data.Name);
// FieldInfo.SetValue 设置对象内指定名称的字段的值
info.SetValue(t, ParseValue(data.Type, data.Data, temp.Name));
}
// FieldInfo.GetValue 获取对象内指定名称的字段的值
object id = temp.GetField("id").GetValue(t);//获取id
FieldInfo dictInfo = container.GetType().GetField("Dict");
object dict = dictInfo.GetValue(container);
bool isExist = (bool)dict.GetType().GetMethod("ContainsKey").Invoke(dict, new System.Object[] { id });
if (isExist)
{
EditorUtility.DisplayDialog("注意!!!", "ID重复:" + id + ",类型: " + container.GetType().Name, "确定");
throw new Exception("ID重复:" + id + ",类型: " + container.GetType().Name);
}
dict.GetType().GetMethod("Add").Invoke(dict, new System.Object[] { id, t });
}
IFormatter f = new BinaryFormatter();
Stream s = new FileStream(BinDataPath + "/" + container.GetType().Name + ".bytes", FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
f.Serialize(s, container);
Debug.Log("已生成 " + BinDataPath + "/" + container.GetType().Name + ".bytes ");
s.Close();
}
public static object ParseValue(string type, string data, string classname)
{
object o = null;
try
{
switch (type)
{
case SupportType.INT:
o = int.Parse(data);
break;
case SupportType.LONG:
o = long.Parse(data);
break;
case SupportType.FLOAT:
o = float.Parse(data);
break;
case SupportType.STRING:
o = data;
break;
case SupportType.LIST_INT:
data = data.Substring(1, data.Length - 2);//移除 '[' ']'
string[] ints = data.Split(',');//逗号分隔
List list = new List();
foreach (var item in ints)
{
list.Add(int.Parse(item));
}
o = list;
break;
case SupportType.LIST_FLOAT:
data = data.Substring(1, data.Length - 2);//移除 '[' ']'
string[] floats = data.Split(',');//逗号分隔
List list2 = new List();
foreach (var item in floats)
{
list2.Add(float.Parse(item));
}
o = list2;
break;
case SupportType.LIST_STRING:
data = data.Substring(1, data.Length - 2);//移除 '[' ']'
string[] strs = data.Split(',');//逗号分隔
o = strs;
break;
case SupportType.LIST_LIST_INT:
break;
case SupportType.LIST_LIST_FLOAT:
break;
case SupportType.LIST_LIST_STRING:
break;
case SupportType.DICTIONARY_INT_INT:
break;
case SupportType.DICTIONARY_INT_FLOAT:
break;
case SupportType.DICTIONARY_INT_STRING:
break;
case SupportType.DICTIONARY_INT_LIST_INT:
break;
case SupportType.DICTIONARY_INT_LIST_FLOAT:
break;
}
}
catch (Exception ex)
{
ProgressBar.HideBarWithFailInfo("\n" + "错误的数据值:" + data + "\n位于:" + classname);
throw new Exception("\n错误的数据值:" + data + "\n位于:" + classname, ex);
}
return o;
}
}
}
脚本生成器
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System;
using UnityEngine;
using System.IO;
using System.Reflection;
namespace WJExcelDataManager
{
public class SupportType
{
public const string INT = "i";
public const string LONG = "l";
public const string FLOAT = "f";
public const string STRING = "s";
public const string LIST_INT = "ai";
public const string LIST_FLOAT = "af";
public const string LIST_STRING = "as";
public const string LIST_LIST_INT = "aai";
public const string LIST_LIST_FLOAT = "aaf";
public const string LIST_LIST_STRING = "aas";
public const string DICTIONARY_INT_INT = "i_i";
public const string DICTIONARY_INT_FLOAT = "i_f";
public const string DICTIONARY_INT_STRING = "i_s";
public const string DICTIONARY_INT_LIST_INT = "i_ai";
public const string DICTIONARY_INT_LIST_FLOAT = "i_af";
}
//脚本生成器
public class ScriptGenerator
{
private string[] Names;
private string[] Types;
private string ClassName;
private string InputPath;
public ScriptGenerator(string inputPath, string className, string[] fileds, string[] types)
{
InputPath = inputPath;
ClassName = className;
Names = fileds;
Types = types;
}
//开始生成脚本
public string Generate()
{
if (Types == null || Names == null || ClassName == null)
throw new Exception("表名:" + ClassName +
"\n表名为空:" + (ClassName == null) +
"\n字段类型为空:" + (Types == null) +
"\n字段名为空:" + (Names == null));
return CreateCode(ClassName, Types, Names);
}
//创建代码。
private string CreateCode(string ClassName, string[] types, string[] fields)
{
//生成类
StringBuilder classSource = new StringBuilder();
classSource.Append("/*Auto create\n");
classSource.Append("Don't Edit it*/\n");
classSource.Append("\n");
classSource.Append("using System;\n");
classSource.Append("using System.Reflection;\n");
classSource.Append("using System.Collections.Generic;\n");
classSource.Append("namespace " + ExcelDataTool.CODE_NAMESPACE + "\n");
classSource.Append("{\n");
classSource.Append("[Serializable]\n");
classSource.Append("public class " + ClassName + "Item\n");//表里每一条数据的类型名为表类型名加Item
classSource.Append("{\n");
//设置成员
for (int i = 0; i < fields.Length; ++i)
{
classSource.Append(PropertyString(types[i], fields[i]));
}
classSource.Append("}\n");
//生成Container
classSource.Append("\n");
classSource.Append("[Serializable]\n");
classSource.Append("public class " + ClassName + "\n");
classSource.Append("{\n");
string idType = "";
for (int i = 0; i < fields.Length; i++)
{
if (fields[i] == "id" || fields[i] == "ID" || fields[i] == "iD" || fields[i] == "Id")
{
idType = GetTrueType(types[i]);
break;
}
}
classSource.Append("\tpublic " + "Dictionary<" + idType + ", " + ClassName + "Item" + " > " + " Dict" + " = new Dictionary<" + idType + ", " + ClassName + "Item" + ">();\n");
classSource.Append("}\n");
classSource.Append("}\n");
return classSource.ToString();
/* //生成的条目数据类
namespace WJExcelDataClass
{
public class testItem
{
public int id;
public float m_float;
public string str;
public test();
}
}
//生成的表数据类
using System.Collections.Generic;
namespace WJExcelDataClass
{
public class test
{
public Dictionary Dict;
public testContainer();
}
}
*/
}
private string PropertyString(string type, string propertyName)
{
if (string.IsNullOrEmpty(type) || string.IsNullOrEmpty(propertyName))
return null;
type = GetTrueType(type);
if (!string.IsNullOrEmpty(type))
{
StringBuilder sbProperty = new StringBuilder();
sbProperty.Append("\tpublic " + type + " " + propertyName + ";\n");
return sbProperty.ToString();
}
else
{
return "";
}
}
private string GetTrueType(string type)
{
switch (type)
{
case SupportType.INT:
type = "int";
break;
case SupportType.LONG:
type = "long";
break;
case SupportType.FLOAT:
type = "float";
break;
case SupportType.STRING:
type = "string";
break;
case SupportType.LIST_INT:
type = "List";
break;
case SupportType.LIST_FLOAT:
type = "List";
break;
case SupportType.LIST_STRING:
type = "List";
break;
case SupportType.LIST_LIST_INT:
break;
case SupportType.LIST_LIST_FLOAT:
break;
case SupportType.LIST_LIST_STRING:
break;
case SupportType.DICTIONARY_INT_INT:
break;
case SupportType.DICTIONARY_INT_FLOAT:
break;
case SupportType.DICTIONARY_INT_STRING:
break;
case SupportType.DICTIONARY_INT_LIST_INT:
break;
case SupportType.DICTIONARY_INT_LIST_FLOAT:
break;
default:
ProgressBar.HideBarWithFailInfo("\n输入了错误的数据类型: " + type + ", 类名: " + ClassName + ", 位于: " + InputPath);
throw new Exception("输入了错误的数据类型: " + type + ", 类名: " + ClassName + ", 位于: " + InputPath);
}
return type;
}
//创建数据管理器脚本
public static void CreateDataManager(Assembly assembly)
{
List list = new List();
list.AddRange(assembly.GetTypes());
IEnumerable types = list.FindAll(t => { return !t.Name.Contains("Item"); });
StringBuilder source = new StringBuilder();
source.Append("/*\n");
source.Append(" * This file was generated by a tool.\n");
source.Append(" * Do not edit it, otherwise the changes will be overwritten.\n");
source.Append(" */\n");
source.Append("\n");
source.Append("using System;\n");
source.Append("using UnityEngine;\n");
source.Append("using System.Runtime.Serialization;\n");
source.Append("using System.Runtime.Serialization.Formatters.Binary;\n");
source.Append("using System.IO;\n");
source.Append("using " + ExcelDataTool.CODE_NAMESPACE + ";\n\n");
source.Append("[Serializable]\n");
source.Append("public class DataManager //: SingletonTemplate\n");
source.Append("{\n");
//定义变量
foreach (Type t in types)
{
source.Append("\tpublic " + t.Name + " p_" + t.Name + ";\n");
}
source.Append("\n");
//定义方法
foreach (Type t in types)
{
string typeName = t.Name + "Item";//类型名
string funcName = typeName.Remove(1).ToUpper() + typeName.Substring(1);//将类型名第一个字母大写
List fields = new List();
fields.AddRange(list.Find(temp => temp.Name == typeName).GetFields());//获取数据类的所有字段信息
string idType = fields.Find(f => f.Name == "id" || f.Name == "ID" || f.Name == "iD" || f.Name == "Id").FieldType.Name;//获取id的数据类型
source.Append("\tpublic " + typeName + " Get" + funcName + "ByID" + "(" + idType + " id)\n");
source.Append("\t{\n");
source.Append("\t\t" + typeName + " t = null;\n");
source.Append("\t\tp_" + t.Name + ".Dict.TryGetValue(id, out t);\n");
source.Append("\t\tif (t == null) Debug.LogError(" + '"' + "can't find the id " + '"' + " + id " + "+ " + '"' + " in " + t.Name + '"' + ");\n");
source.Append("\t\treturn t;\n");
source.Append("\t}\n\n");
}
加载所有配置表
source.Append("\tpublic void LoadAll()\n");
source.Append("\t{\n");
foreach (Type t in types)
{
source.Append("\t\tp_" + t.Name + " = Load(" + '"' + t.Name + '"' + ") as " + t.Name + ";\n");
}
source.Append("\t}\n\n");
//反序列化
source.Append("\tprivate System.Object Load(string name)\n");
source.Append("\t{\n");
source.Append("\t\tIFormatter f = new BinaryFormatter();\n");
source.Append("\t\tTextAsset text = Resources.Load(" + '"' + ExcelDataTool.BinDataFolder + "/" + '"' + " + name);\n");
source.Append("\t\tStream s = new MemoryStream(text.bytes);\n");
source.Append("\t\tSystem.Object obj = f.Deserialize(s);\n");
source.Append("\t\ts.Close();\n");
source.Append("\t\treturn obj;\n");
source.Append("\t}\n");
source.Append("}\n");
//保存脚本
string path = System.Environment.CurrentDirectory + "/Assets/Scripts/ExcelDataManager";
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
StreamWriter sw = new StreamWriter(path + "/DataManager.cs");
sw.WriteLine(source.ToString());
Debug.Log("已生成 " + path + "/DataManager.cs ");
sw.Close();
/* //生成的数据管理类如下
using System;
using UnityEngine;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using ExcelConfigClass;
[Serializable]
public class DataManager //: SingletonTemplate
{
public test p_test;
public test2 p_test2;
public testItem GetTestItemByID(Int32 id)
{
testItem t = null;
p_test.Dict.TryGetValue(id, out t);
if (t == null) Debug.LogError("can't find the id " + id + " in test");
return t;
}
public test2Item GetTest2ItemByID(String id)
{
test2Item t = null;
p_test2.Dict.TryGetValue(id, out t);
if (t == null) Debug.LogError("can't find the id " + id + " in test2");
return t;
}
public void LoadAll()
{
p_test = Load("test") as test;
p_test2 = Load("test2") as test2;
}
private System.Object Load(string name)
{
IFormatter f = new BinaryFormatter();
TextAsset text = Resources.Load("BinConfigData/" + name);
Stream s = new MemoryStream(text.bytes);
System.Object obj = f.Deserialize(s);
s.Close();
return obj;
}
}
*/
}
}
}
导表路径
using UnityEngine;
using UnityEditor;
namespace WJExcelDataManager
{
public class PathWindow : EditorWindow
{
static EditorWindow window;
[MenuItem("开发工具/导表工具/设置导表路径")]
public static void Init()
{
window = EditorWindow.GetWindow(typeof(PathWindow));
window.titleContent.text = "设置路径";
window.ShowTab();
window.Show();
}
private string m_Path;
void OnGUI()
{
EditorGUILayout.LabelField("", GUILayout.MaxHeight(5f));
EditorGUILayout.LabelField("当前路径", GUILayout.MinHeight(20f));
EditorGUILayout.LabelField(PlayerPrefs.GetString(System.Environment.CurrentDirectory + "ExcelDataInputPath", ""), GUILayout.MinHeight(20f));
if (GUILayout.Button("选择路径"))
{
string oldpath = PlayerPrefs.GetString(System.Environment.CurrentDirectory + "ExcelDataInputPath", "");
string path = EditorUtility.OpenFolderPanel("设置导表路径", oldpath, "");//打开文件夹选择面板
if (!string.IsNullOrEmpty(path))
{
PlayerPrefs.SetString(System.Environment.CurrentDirectory + "ExcelDataInputPath", path);
Debug.Log("新的excel文件路径: " + "" + path + " ");
EditorUtility.DisplayDialog("Set Excel Data Input Path", "\n新的excel文件路径: " + path, "OK");
window.Repaint();
}
}
}
}
}
进度条
using UnityEditor;
namespace WJExcelDataManager
{
public class ProgressBar : EditorWindow
{
void OnInspectorUpdate() //更新
{
Repaint(); //重新绘制
}
public static void UpdataBar(string info, float progress)
{
//使用这句代码,在进度条后边会有一个 关闭按钮,但是用这句话会直接卡死,切记不要用
//EditorUtility.DisplayCancelableProgressBar("Loading Excel Data", info + "...", progress);
//使用这句创建一个进度条, 参数1 为标题,参数2为提示,参数3为 进度百分比 0~1 之间
EditorUtility.DisplayProgressBar("导表工具", info + "...", progress);
if (progress >= 1)
{
EditorUtility.ClearProgressBar();
EditorUtility.DisplayDialog("导表工具", info, "确定");
}
}
public static void HideBarWithFailInfo(string failinfo)
{
EditorUtility.ClearProgressBar();
EditorUtility.DisplayDialog("注意!!!", failinfo, "确定");
}
}
}
使用ExcelDataReader读写excel(.xls,.xlsx)
https://blog.csdn.net/chen_victor/article/details/70140925
源代码:unity导表工具-CSDN下载 https://download.csdn.net/download/oldherowang/10653750