文章摘要
TableExportTool 是一个用于表格数据导出的工具,主要包含表格读取、数据解析、导出和代码生成四大模块。它支持读取 Excel/CSV 文件,解析字段和类型后转换为 JSON、二进制、Lua 等多种格式,并自动生成 C#、Lua 等数据结构代码。工具还提供 Unity 集成功能,支持一键导出、Asset生成和热更新。核心流程包括读取表头、类型校验、数据组装和导出,通过 NPOI/EPPlus 实现表格解析,并支持自定义类型转换和外键校验。典型应用场景是游戏配置表管理,可扩展多语言和嵌套结构等高级功能。
TableExportTool 主要由以下几个模块组成:
使用 NPOI 或 EPPlus 读取 Excel 文件。
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
public IWorkbook LoadWorkbook(string path)
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
return new XSSFWorkbook(fs);
}
}
ISheet sheet = workbook.GetSheetAt(0);
IRow fieldRow = sheet.GetRow(0); // 字段名
IRow typeRow = sheet.GetRow(1); // 类型
List<string> fieldNames = new List<string>();
List<string> fieldTypes = new List<string>();
for (int i = 0; i < fieldRow.LastCellNum; i++)
{
fieldNames.Add(fieldRow.GetCell(i).StringCellValue.Trim());
fieldTypes.Add(typeRow.GetCell(i).StringCellValue.Trim());
}
List<Dictionary<string, object>> dataList = new List<Dictionary<string, object>>();
for (int i = 2; i <= sheet.LastRowNum; i++)
{
IRow row = sheet.GetRow(i);
if (row == null) continue;
// 跳过注释/空行
if (row.GetCell(0) == null || row.GetCell(0).ToString().StartsWith("#")) continue;
Dictionary<string, object> item = new Dictionary<string, object>();
for (int j = 0; j < fieldNames.Count; j++)
{
string type = fieldTypes[j];
var cell = row.GetCell(j);
object value = ConvertCell(cell, type);
item[fieldNames[j]] = value;
}
dataList.Add(item);
}
object ConvertCell(ICell cell, string type)
{
if (cell == null) return GetDefault(type);
string str = cell.ToString();
switch (type)
{
case "int": return int.TryParse(str, out var i) ? i : 0;
case "float": return float.TryParse(str, out var f) ? f : 0f;
case "bool": return str == "1" || str.ToLower() == "true";
case "string": return str;
case "array_int": return str.Split('|').Select(s => int.Parse(s)).ToArray();
// 可扩展 array_float, array_string, enum, struct 等
default: return str;
}
}
string json = JsonConvert.SerializeObject(dataList, Formatting.Indented);
File.WriteAllText($"{outDir}/{sheetName}.json", json, Encoding.UTF8);
可用 protobuf、MessagePack、自己写二进制序列化等。
根据字段名和类型自动生成 C# 数据结构:
public class {TableName}
{
public int id;
public string name;
public int hp;
public int[] skills;
}
实现方式:用模板字符串,遍历字段名和类型,拼接生成代码文件。
同理,生成 Lua table、TS interface 等。
[MenuItem("Tools/TableExportTool/Export All")]
挂载一键导出入口。/TableExportTool/
/Editor/
TableExportTool.cs
ExcelReader.cs
CodeGenerator.cs
/ConfigTables/
Hero.xlsx
Weapon.xlsx
/Exported/
Hero.json
Weapon.json
Hero.cs
Weapon.cs
TableExportTool 的详细实现核心是:
下面我将针对你关心的类型扩展、代码生成模板、Unity集成三部分,分别给出详细的实现思路和关键代码示例。以 C#(Unity 环境)为例,便于你直接集成到实际项目中。
在类型解析和转换时,增加对 array_float
的支持:
object ConvertCell(ICell cell, string type)
{
if (cell == null) return GetDefault(type);
string str = cell.ToString();
switch (type)
{
case "int": return int.TryParse(str, out var i) ? i : 0;
case "float": return float.TryParse(str, out var f) ? f : 0f;
case "bool": return str == "1" || str.ToLower() == "true";
case "string": return str;
case "array_int": return str.Split('|').Where(s => !string.IsNullOrEmpty(s)).Select(s => int.Parse(s)).ToArray();
case "array_float": return str.Split('|').Where(s => !string.IsNullOrEmpty(s)).Select(s => float.Parse(s)).ToArray();
// 下面是enum和struct的例子
case "enum": return str; // 你可以在代码中定义enum类型并转换
case "struct": return ParseStruct(str); // 见下
default: return str;
}
}
假设你有一列类型为 enum
,如 Color
,Excel中填 Red
、Blue
等。你可以在代码生成时自动生成 C# enum,并在数据加载时用 Enum.Parse 转换。
// 代码生成时
public enum Color
{
Red,
Blue,
Green
}
// 数据加载时
case "enum": return Enum.Parse(typeof(Color), str);
假设 Excel 某列类型为 struct
,内容如 1,2,3
,你定义一个结构体:
public struct Vector3
{
public float x, y, z;
public Vector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
}
Vector3 ParseStruct(string str)
{
var parts = str.Split(',');
return new Vector3(
float.Parse(parts[0]),
float.Parse(parts[1]),
float.Parse(parts[2])
);
}
假设你有如下表头:
字段名 | id | name | hp | skills | color | pos |
---|---|---|---|---|---|---|
类型 | int | string | int | array_int | enum | struct |
你可以用如下模板生成 C# 类:
public class {ClassName}
{
{Fields}
}
自动生成字段:
string GetCSharpType(string type)
{
switch (type)
{
case "int": return "int";
case "float": return "float";
case "bool": return "bool";
case "string": return "string";
case "array_int": return "int[]";
case "array_float": return "float[]";
case "enum": return "Color"; // 假设enum名为Color
case "struct": return "Vector3";
default: return "string";
}
}
StringBuilder sb = new StringBuilder();
sb.AppendLine($"public class {className}");
sb.AppendLine("{");
for (int i = 0; i < fieldNames.Count; i++)
{
string type = GetCSharpType(fieldTypes[i]);
sb.AppendLine($" public {type} {fieldNames[i]};");
}
sb.AppendLine("}");
File.WriteAllText($"{outDir}/{className}.cs", sb.ToString());
收集所有 enum 字段的取值,自动生成 enum 代码:
// 假设所有Color列的值为Red, Blue, Green
public enum Color
{
Red,
Blue,
Green
}
public struct Vector3
{
public float x, y, z;
public Vector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; }
}
在 Editor
目录下新建 TableExportToolEditor.cs
:
using UnityEditor;
using UnityEngine;
public class TableExportToolEditor
{
[MenuItem("Tools/TableExportTool/Export All")]
public static void ExportAll()
{
// 1. 扫描表格目录
// 2. 读取并解析表格
// 3. 导出数据和代码
// 4. 刷新AssetDatabase
Debug.Log("表格导出完成!");
}
}
将导出的 JSON/二进制文件放到 Assets/Resources/Config/
,运行时可用:
TextAsset ta = Resources.Load<TextAsset>("Config/Hero");
string json = ta.text;
var heroList = JsonUtility.FromJson<HeroListWrapper>(json);
可在运行时从服务器下载新表,覆盖本地表,重新加载即可。