using System; using System.Collections.Generic; using System.Data; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using ExcelDataReader; using NBC; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEditor; using UnityEngine; using Debug = UnityEngine.Debug; namespace NBF { public static class ExcelToJsonWindow { public static void GenConfig(bool showMessageBox = true) { CfgEditorUtil.GenConfigScripts(); GenConfig(Application.dataPath + "/../Config", showMessageBox); } public static void GenConfig(string path, bool showMessageBox = false) { List list = new List(); GetFiles(path, fileList: ref list); AllJsonData.Clear(); Stopwatch s = Stopwatch.StartNew(); ReadExcel(list.ToArray()); BuildAsset(); s.Stop(); if (showMessageBox) { EditorUtility.DisplayDialog("成功", $"导表完成,耗时{(s.ElapsedMilliseconds / 1000f):.00}秒", "知道了"); EditorUtility.ClearProgressBar(); } else { Debug.Log($"导表完成,耗时{(s.ElapsedMilliseconds / 1000f):.00}秒"); } AssetDatabase.Refresh(); } private static void BuildAsset() { //Application.dataPath var json = JsonConvert.SerializeObject(AllJsonData, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); var jsonObj = JObject.Parse(json); Dictionary tokens = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var item in jsonObj) { try { var name = item.Key; var value = item.Value; if (value != null) { tokens[name] = value; } } catch (Exception e) { Log.Error($"读表异常,请检查,name={item.Key} ex={e}"); } } // 将路径中的所有反斜杠转换为正斜杠 // string normalizedAbsolutePath = savePath.Replace("\\", "/"); // string normalizedDataPath = Application.dataPath.Replace("\\", "/"); // string relativePath = normalizedAbsolutePath.Replace(normalizedDataPath, ""); // ConfigAssets.SavePath // var relativePath = "Assets/ResRaw/config/ConfigAssets.asset"; //$"Assets/Resources/Config/ConfigAssets.asset"; var relativePath = ConfigAssets.SavePath; var asset = EditorUtils.GetOrCreateAsset(relativePath); var types = Reflection.GetAllNonAbstractDerivedTypes(); foreach (var type in types) { var tableNameAttribute = type.GetCustomAttribute(); if (tableNameAttribute == null) continue; if (!tokens.TryGetValue(tableNameAttribute.Name, out var token)) { Log.Warning($"{tableNameAttribute.Name} 解析失败,tableName不一致"); continue; } if (token is not JArray jArray) return; asset.Parse(jArray.ToArray(), type); } EditorUtility.SetDirty(asset); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } #region 文件读写 private static void GetFiles(string path, ref List fileList) { var files = Directory.GetFiles(path); if (files.Length > 0) { foreach (var file in files) { var fileName = Path.GetFileName(file); if ((fileName.EndsWith(".xls", true, CultureInfo.CurrentCulture) || fileName.EndsWith(".xlsx", true, CultureInfo.CurrentCulture)) && !fileName.StartsWith(".") && !fileName.StartsWith("~")) { fileList.Add(file.Replace('\\', '/')); } } } } #endregion #region Excel读取 private static readonly Dictionary AllJsonData = new Dictionary(); private static bool _buildIng = false; private static int _buildTotal = 0; private static int _buildDoneCount = 0; private static void ReadExcel(IReadOnlyCollection paths) { _buildTotal = paths.Count; _buildDoneCount = 0; _buildIng = true; foreach (var t in paths) { ReadSingleExcel(t); } _buildIng = false; } private static void ReadSingleExcelFunc(string xlsxPath, bool isThrow = false) { try { using FileStream stream = File.Open(xlsxPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream); DataSet result = excelReader.AsDataSet(); // 读取第一张工资表 DataTableCollection tc = result.Tables; if (tc.Count >= 1) { foreach (DataTable table in tc) { if (!table.TableName.StartsWith("!") && !table.TableName.StartsWith("Sheet")) { ReadSingleSheet(table, isThrow); } } } else { Debug.LogError("表名为空,请检查表"); } } catch (Exception e) { Log.Error(e); } } public static void ReadSingleExcel(string xlsxPath, bool isThrow = false) { Stopwatch s = Stopwatch.StartNew(); string xlsxName = Path.GetFileName(xlsxPath); if (!File.Exists(xlsxPath)) { Debug.LogError("不存在该Excel!"); return; } try { ReadSingleExcelFunc(xlsxPath, isThrow); Debug.Log(xlsxName + ".xlsx" + "转换Json成功!"); s.Stop(); Debug.Log($"导表{xlsxName},花费时间={s.ElapsedMilliseconds}毫秒"); _buildDoneCount++; } catch (Exception e) { Debug.LogError(e); } } /// /// 读取一个工作表的数据 /// /// 读取的工作表数据 /// 是否抛出异常 private static void ReadSingleSheet(DataTable dataTable, bool isThrow = false) { int rows = dataTable.Rows.Count; int columns = dataTable.Columns.Count; if (rows < 1 || columns < 1) { return; } // 工作表的行数据 DataRowCollection collect = dataTable.Rows; // xlsx对应的数据字段,规定是第二行 string[] jsonFields = new string[columns]; string[] jsonFieldsType = new string[columns]; // 要保存成Json的obj List objsToSave = new List(); for (int i = 0; i < columns; i++) { //字段 jsonFields[i] = collect[1][i].ToString(); //字段类型 jsonFieldsType[i] = collect[2][i].ToString(); } // _lastTableName = dataTable.TableName; // 从第三行开始 for (int i = 3; i < rows; i++) { Dictionary jObject = new Dictionary(); // _lastX = i; for (int j = 0; j < columns; j++) { var objectValue = collect[i][j].ToString(); // _lastY = j; var lastValue = $"{collect[1][j]}---{objectValue}"; try { if (isThrow) { Debug.Log($"{i}/{j}/{objectValue}"); } Type type = GetTypeByString(jsonFieldsType[j]); // 获取字段 string field = jsonFields[j]; //过滤掉字段为空的值 if (!string.IsNullOrEmpty(field)) { object value = null; if (!string.IsNullOrEmpty(objectValue)) { //需要先判断下是否为数组 if (type != typeof(Array)) { //需要判断一下是否为bool值 bool 直接返回int 0 或者 1 if (type == typeof(bool)) { value = int.Parse(objectValue); } else if (type == typeof(Vector2)) { value = objectValue.ToVector2(); } else if (type == typeof(Vector3)) { value = objectValue.ToVector3(); } else { value = Convert.ChangeType(objectValue, type); } } else { //这里在做二维数组的处理 //一般到这都是Int数组,当然还可以更细致的处理不同类型的数组 string[] strs = objectValue.Split(','); string arrayType = jsonFieldsType[j]; object[] ints = new object[strs.Length]; for (int k = 0; k < strs.Length; k++) { switch (arrayType) { case "[int]": { int.TryParse(strs[k], out var v); ints[k] = v; break; } case "[float]": { float.TryParse(strs[k], out var v); ints[k] = v; break; } case "[string]": ints[k] = strs[k]; break; default: ints[k] = strs[k]; break; } } value = ints; } } else { // Log.I($"{dataTable.TableName} 字段{field}有空值存在 第{j + 1}列数据 请检查表格!!!"); //如果值为空会出现导表失败 这里需要做一下处理 value = GetDataType(type); } jObject[field] = value; } } catch (Exception) { Debug.LogError($"解析表错误,table={dataTable.TableName} y={i} x={j} value={lastValue}"); } } objsToSave.Add(jObject); } AllJsonData[dataTable.TableName] = objsToSave; } /// /// 判断一下数据类型 /// /// /// public static object GetDataType(Type type) { object value = null; if (type == typeof(int) || type == typeof(float)) { value = 0; } else if (type == typeof(string)) { value = ""; } else if (type == typeof(bool)) { //如果boo值类型为空 默认显示 value = 0; } else if (type == typeof(Array)) { value = new List().ToArray(); } else { value = ""; } return value; } /// /// 获取字段类型 /// /// /// public static Type GetTypeByString(string type) { switch (type.ToLower()) { case "bool": return typeof(bool); case "array": return typeof(Array); case "double": return typeof(double); case "float": return typeof(float); case "int": return typeof(int); case "long": return typeof(long); case "object": return typeof(object); case "string": return typeof(string); case "[float]": return typeof(Array); case "[int]": return typeof(Array); case "[string]": return typeof(Array); case "v2": return typeof(Vector2); case "v3": return typeof(Vector3); default: return typeof(string); } } #endregion } }