文章摘要
该工具类提供Excel到Json的转换功能,主要包含:
ExcelToJsonToolEditor:核心编辑器类,处理Excel批量/单表转换、文件路径操作
类型定义:TypeValue存储字段信息,JsonKeyType标识键类型
转换流程:读取Excel→生成Json→自动创建对应C#配置类代码
扩展性:支持指定表转换、错误处理、代码模板化生成
关键方法:DoXlsxToJson处理单表转换,GenerateSheetCsharpCode动态生成配置类代码。适用于Unity项目配置表自动化处理,需配合Newtonsoft.Json库使用。
下面是整理后的Excel转Json工具类,结构更清晰,注释更详细,便于维护和扩展。
主要分为:
你可以根据实际项目需求进一步扩展类型、增加校验、优化代码生成等。
#if UNITY_EDITOR
using Excel;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using UnityEditor;
using UnityEngine;
///
/// Excel转Json工具的Editor扩展
///
[CustomEditor(typeof(ExcelToJsonTool))]
public class ExcelToJsonToolEditor : Editor
{
private string _curFinishText = "";
private string _excelName = "";
private string _csharpText = "";
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
_excelName = EditorGUILayout.TextField("Excel名(不填全转):", _excelName);
if (GUILayout.Button("Excel表转Json"))
{
_curFinishText = "";
DoAllXlsxToJson(_excelName);
_curFinishText += "转表结束...";
}
else if (GUILayout.Button("打开Excel文件夹"))
{
string path = System.Environment.CurrentDirectory + "/Excel/init.txt";
EditorUtility.RevealInFinder(path);
}
else if (GUILayout.Button("打开Json文件夹"))
{
string path = System.Environment.CurrentDirectory + "/Assets/Resources/Config/init.txt";
EditorUtility.RevealInFinder(path);
}
EditorGUILayout.BeginHorizontal();
GUILayout.Label(_curFinishText);
EditorGUILayout.EndHorizontal();
}
///
/// 批量或单个Excel转Json
///
void DoAllXlsxToJson(string singleExcel = "")
{
string filepath = Application.dataPath + "/Script/Tools/JsonClass.cs";
_csharpText = File.ReadAllText(filepath);
ExcelToJsonTool excels = target as ExcelToJsonTool;
foreach (ExcelConfig excelName in excels._Excels)
{
if (excelName.name.Equals(singleExcel) || string.IsNullOrEmpty(singleExcel))
{
_curFinishText += "读取Excel:" + excelName.name + "\n";
DoXlsxToJson(excelName);
_curFinishText += "\n";
if (!string.IsNullOrEmpty(singleExcel))
break;
}
}
// 写回C#代码
if (File.Exists(filepath))
{
File.SetAttributes(filepath, FileAttributes.Normal);
File.WriteAllText(filepath, _csharpText);
}
}
///
/// 单个Excel转Json
///
private bool DoXlsxToJson(ExcelConfig config)
{
string dataPath = System.Environment.CurrentDirectory;
string xlsxPath = dataPath + "/Excel/" + config.name + ".xlsx";
string savePath = Application.dataPath + "/Resources/Config/";
FileStream stream = null;
try
{
stream = File.Open(xlsxPath, FileMode.Open, FileAccess.Read);
}
catch (IOException)
{
_curFinishText = "请关闭Excel后再进行";
if (stream != null) stream.Close();
return false;
}
if (stream == null)
return false;
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
excelReader.Read(); // 必须先Read一次
DataSet result = excelReader.AsDataSet();
if (result == null)
{
Debug.LogError(config.name + " Has Empty Line!");
return false;
}
foreach (string exSheet in config.sheets)
{
List<TypeValue> kvList = new List<TypeValue>();
JsonKeyType idType = ReadSingleSheet(result.Tables[exSheet], savePath + exSheet + ".json", ref kvList);
GenerateSheetCSharpCode(exSheet, kvList, idType);
_curFinishText += $"----转表:{exSheet}.json完成\n";
}
stream.Close();
return true;
}
///
/// 生成C#代码
///
private void GenerateSheetCSharpCode(string sheetName, List<TypeValue> memberList, JsonKeyType keyType)
{
int startIndex = _csharpText.IndexOf("ConfigDefine");
int endIndex = _csharpText.IndexOf("}", startIndex) + 1;
string defineString = _csharpText.Substring(startIndex, endIndex - startIndex);
string strHas = sheetName + " = ";
if (defineString.Contains(strHas))
return; // 已有定义
string classDefine = EditorUtil.format(UICodeConfig.classDefine, sheetName, sheetName);
_csharpText = _csharpText.Insert(endIndex - 1, classDefine);
// 类定义
startIndex = _csharpText.IndexOf("ConfigDefine");
endIndex = _csharpText.IndexOf("}", startIndex) + 1;
string configStr = sheetName + "Config";
string inhiertStr = $"Config<{configStr}>";
string className = $"\npublic class {configStr} : {inhiertStr}";
List<string> memberStrList = new List<string>();
foreach (var member in memberList)
{
string memberStr = EditorUtil.format(UICodeConfig.variable, member.memberType, member.memberName);
memberStrList.Add(memberStr);
}
string finalMemberStr = string.Join("\n", memberStrList);
className += $"\n{{\n{finalMemberStr}";
// 构造函数
string keyTypeStr = keyType == JsonKeyType.INT ? "JsonKeyType.INT" : "JsonKeyType.STRING";
string constructStr = EditorUtil.format(UICodeConfig.construct, configStr, sheetName, keyTypeStr) + " { }";
className += $"\n\t{constructStr}\n}}\n";
_csharpText = _csharpText.Insert(endIndex + 1, className);
}
///
/// 读取单个Sheet并导出Json
///
private JsonKeyType ReadSingleSheet(DataTable dataTable, string jsonPath, ref List<TypeValue> kvList)
{
JsonKeyType type = JsonKeyType.INT;
kvList = new List<TypeValue>();
int rows = dataTable.Rows.Count;
int columns = dataTable.Columns.Count;
DataRowCollection collect = dataTable.Rows;
// 字段名和类型
string[] jsonFields = new string[columns];
string[] jclassTypes = new string[columns];
for (int i = 0; i < columns; i++)
{
jsonFields[i] = collect[1][i].ToString();
jclassTypes[i] = collect[2][i].ToString();
string memberName = jsonFields[i];
if (memberName.Equals("ID") || memberName.Equals("IDs"))
continue;
TypeValue kv = new TypeValue
{
memberName = jsonFields[i],
memberType = GetCSharpType(jclassTypes[i])
};
kvList.Add(kv);
}
// 数据行
List<object> objsToSave = new List<object>();
for (int i = 3; i < rows; i++)
{
JObject postedJObject = new JObject();
bool isEnd = false;
for (int j = 0; j < columns; j++)
{
string memberName = jsonFields[j];
string classType = jclassTypes[j];
string str = collect[i][j].ToString();
object value = ParseCellValue(classType, str);
if (memberName.Equals("IDs"))
type = JsonKeyType.STRING;
if (memberName.Equals("ID") || memberName.Equals("IDs"))
{
if (string.IsNullOrEmpty(str))
{
isEnd = true;
break;
}
}
postedJObject.Add(memberName, JToken.FromObject(value));
}
if (isEnd)
break;
objsToSave.Add(postedJObject);
}
// 保存为Json
string content = JsonConvert.SerializeObject(objsToSave, Formatting.Indented);
SaveFile(content, jsonPath);
return type;
}
///
/// 类型映射
///
private string GetCSharpType(string excelType)
{
switch (excelType)
{
case "String": return "string";
case "Int": return "int";
case "ArrayInt":
case "ArrayIntLine": return "int[]";
case "ArrayString":
case "StringArrayLine": return "string[]";
case "Float": return "float";
case "ArrayFloat": return "float[]";
default: return "string";
}
}
///
/// 单元格值解析
///
private object ParseCellValue(string classType, string str)
{
switch (classType)
{
case "String":
return str;
case "Int":
return EditorUtil.ParseInt(str);
case "ArrayInt":
return string.IsNullOrEmpty(str) ? new int[0] : Array.ConvertAll(str.Split(','), EditorUtil.ParseInt);
case "ArrayIntLine":
return string.IsNullOrEmpty(str) ? new int[0] : Array.ConvertAll(str.Split('|'), EditorUtil.ParseInt);
case "ArrayString":
return string.IsNullOrEmpty(str) ? new string[0] : str.Split(',');
case "StringArrayLine":
return string.IsNullOrEmpty(str) ? new string[0] : str.Split('|');
case "Float":
return EditorUtil.ParseFloat(str);
case "ArrayFloat":
return string.IsNullOrEmpty(str) ? new float[0] : Array.ConvertAll(str.Split(','), EditorUtil.ParseFloat);
default:
return str;
}
}
///
/// 保存文件
///
private void SaveFile(string content, string jsonPath)
{
if (File.Exists(jsonPath))
File.Delete(jsonPath);
using (var streamWriter = new StreamWriter(new FileStream(jsonPath, FileMode.Create)))
{
streamWriter.Write(content);
}
}
// 类型定义
public class TypeValue
{
public string memberType;
public string memberName;
}
public enum JsonKeyType
{
INT,
STRING
}
public class UICodeConfig
{
public const string classDefine = "\tpublic static string {0} = \"{1}\";\n";
public const string variable = "\tpublic {0} {1};";
public const string construct = "public {0}() : base(ConfigDefine.{1},{2})";
}
}
///
/// 编辑器工具方法
///
public static class EditorUtil
{
public static int ParseInt(string data)
{
if (string.IsNullOrEmpty(data))
return 0;
int val;
return int.TryParse(data, out val) ? val : 0;
}
public static float ParseFloat(string data)
{
if (string.IsNullOrEmpty(data))
return 0f;
float val;
return float.TryParse(data, out val) ? val : 0f;
}
public static string format(string valuestr, params object[] paramStrs)
{
try
{
return string.Format(valuestr, paramStrs);
}
catch (Exception)
{
#if UNITY_EDITOR
Debug.LogError(string.Format(": {0} 参数数量不匹配", valuestr));
#endif
return valuestr;
}
}
}
#endif
GetCSharpType
和ParseCellValue
中扩展即可。