功能说明:根据 .xlsx
文件生成对应的 JSON 文件,并自动创建脚本
注意事项
-
Excel 读取依赖
本功能依赖
EPPlus
库,只能读取.xlsx
文件 。请确保将该脚本放置在Assets
目录下的Editor
文件夹中。同时,在Editor
下再创建一个Excel
目录,并将你的.xlsx
文件放到Excel
目录下。注意 :该目录下只能有一个.xlsx
文件,且该文件是唯一的数据源。- Excel 文件格式要求 :
- 第一行:字段名(与自动生成脚本中的字段对应)。
- 第二行:中文注释。
- 第三行:字段的数据类型(目前支持
int
、float
、double
、string
、bool
和数组类型,如int[]
、string[]
)。 - 第四行开始:实际数据。
- Epplus依赖获取查看我的另一篇文章Nuget For Unity插件介绍_nugetforunity-CSDN博客
- Excel 文件格式要求 :
-
生成脚本与 JSON 文件
使用编辑器中的 ExcelTool 进行生成,点击 读取 Excel 按钮后,将自动执行以下操作:
- 删除之前生成的脚本目录和 JSON 目录(如果存在),然后重新生成它们。
- 注意:在这两个目录下请不要放置其他文件,因为此工具会在每次生成时覆盖这些目录。
-
关于数组格式
数组数据需要按如下格式写入:
例如 :1|2|3
。数组成员使用|
分隔。 -
支持多 sheet
一个
.xlsx
文件中可以包含多个 sheet。在读取时,工具会读取所有 sheet 数据。请注意:- 将每个 sheet 的名字改为与要生成的脚本名一致。
- Sheet 名称必须符合 C# 的命名规范,建议使用
TB_
开头。
提示
- 脚本命名:请确保每个 sheet 的名字与生成的 C# 脚本的名称一致。
- 字段类型 :当前支持的字段类型包括基本数据类型(
int
、float
、double
、string
、bool
)以及数组类型(如int[]
、string[]
)。 - 感谢原作者 :特别感谢原作者"小人"的贡献,我仅添加了一些功能,以下是他在 B站的教程视频地址:Unity中简单根据excel文件自动生成对应的C#脚本及json文件_哔哩哔哩_bilibili
格式
保证这个目录格式
Excel格式
源码
cs
using OfficeOpenXml;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
using System;
using UnityEditor;
using UnityEngine;
/*
功能:根据.xlsx文件生成对应的json文件,并自动创建脚本
注意:
一 Excel读取依赖EPPlus,只能读.xlsx文件,Asset下创建一个Editor,同时将该脚本放到Editor下
然后在Editor下再创建一个Excel目录,然后将你的Excel文件放到Excel目录下,注意Excel目录下
只能有一个Excel文件,同时Excel格式要符合下列要求,最后Excel是唯一的数据源,所以除了保护好
你的Excel文件外,其他的可以重新生成.使用编辑器上的ExcelTool/读取Excel按钮生成Json和脚本
使用该按钮会删除脚本目录和json目录然后重新生成(如果已经生成过),所以不要在这两个目录下
放置其他文件.
二 xlsx第一行为英文字段与自动生成脚本中的字段对应
第二行为中文注释
第三行为字段的数据类型,目前支持int float double string bool 数组
数组的写法统一为基本数据类型+[] 如int[] 、string[]
第四行开始为实际数据
三 xlsx文件中可以有很多sheet.读取时会读取xlsx的全部sheet
要将对应的sheet的名字改为与要生成的脚本名一致,所以sheet名要符合C#的命名规范,建议使用TB_开头
四 数组的书写格式形如:1|2|3,数组成员使用|分隔
五 感谢原作者小人,我仅做了一些功能补充,下面的地址是小人的b站视频地址
*/
[HelpURL("https://www.bilibili.com/video/BV16f421Q7zA/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click&vd_source=f9b5906b25cd5ca40ec79f317993905b")]
public class ExcelTool
{
//命名空间列表
private static List<string> nameSpaceList = new List<string>()
{ "using System;", "[Serializable]"};
//Root目录,包含Excel本身,Excel生成的json和脚本
private static readonly string excel = Application.dataPath + "/Editor/Excel";
//Excel文件
private static string excelFilePath = excel;
//脚本目录
private static readonly string scriptsFolder = excel + "/ExcelScripts";
//json目录
private static string jsonFolder = excel + "/Json";
[MenuItem("ExcelTool/读取Excel")]
public static void TestTool()
{
// 获取目录下所有以 .xlsx 结尾的文件(不包括子目录中的文件)
string[] files = Directory.GetFiles(excel, "*.xlsx");
if (files.Length > 0)
{
// 获取第一个文件的完整路径
string firstFilePath = files[0];
// 获取文件名(不包括路径)
string fileName = Path.GetFileName(firstFilePath);
excelFilePath = excelFilePath + "/" + fileName;
}
if (!File.Exists(excelFilePath))
{
Debug.LogError("excel文件不存在");
return;
}
CreateDirectory(scriptsFolder);
CreateDirectory(jsonFolder);
var res = ReadExcel(excelFilePath);
for (int i = 0; i < res.Count; i++)
{
string path = scriptsFolder + "/" + res[i].scriptName + ".cs";
CreateAScript(path, res[i].scriptName, nameSpaceList, res[i].fieldType, res[i].fieldName);
CreateAJson(res[i].scriptName, res[i].fieldType, res[i].fieldName, res[i].dataDic);
}
AssetDatabase.Refresh();
}
private static void CreateDirectory(string path)
{
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
else
{
path = ConvertToRelativePath(path);
var b = AssetDatabase.DeleteAsset(path);
Directory.CreateDirectory(path);
}
}
// 将绝对路径转为相对路径
private static string ConvertToRelativePath(string absolutePath)
{
// 获取项目的 'Assets' 文件夹路径
string assetsPath = Application.dataPath;
// 确保返回的路径是相对于 'Assets' 文件夹的
if (absolutePath.StartsWith(assetsPath))
{
// 去掉 Application.dataPath 前缀,返回相对路径
return "Assets" + absolutePath.Substring(assetsPath.Length);
}
Debug.LogError("路径不在 Assets 目录内: " + absolutePath);
return absolutePath;
}
/// <summary>
/// 读取 .xlsx文件,获取第一张sheet的内容
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
private static List<(string scriptName, List<string> fieldType, List<string> fieldName, Dictionary<int, List<string>> dataDic)>
ReadExcel(string path)
{
int sheetsCount;
var list = new List<(string scriptName, List<string> fieldType, List<string> fieldName, Dictionary<int, List<string>> dataDic)>();
FileInfo fileInfo = new FileInfo(path);
using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
{
sheetsCount = excelPackage.Workbook.Worksheets.Count;
}
for (int z = 0; z < sheetsCount; z++)
{
//生成的脚本名
string scriptName;
//字段类型列表
List<string> fieldType = new List<string>();
//字段名列表
List<string> fieldName = new List<string>();
//.xlsx除注释行之外的相关数据
Dictionary<int, List<string>> dataDic = new Dictionary<int, List<string>>();
using (ExcelPackage excelPackage = new ExcelPackage(fileInfo))
{
//取得.xlsx中的第一张sheet(EPPlus中下标从1或者0开始,取决于版本)
ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[z];
if (worksheet == null)
{
sheetsCount++;
continue;
}
//取sheet的名字,即生成的脚本名
scriptName = worksheet.Name;
//取英文字段名
//遍历表格第一行取字段名 注意索引下标
for (int i = 1; i <= worksheet.Dimension.End.Column; i++)
{
if (worksheet.Cells[1, i].Value is null)
{
Debug.LogError($"当前{worksheet}中第1行第{i}个单元格数据为空");
return null;
}
string field = worksheet.Cells[1, i].Value.ToString();
fieldName.Add(field);
}
//取字段类型
//遍历第三行 同上
for (int i = 1; i <= worksheet.Dimension.End.Column; i++)
{
if (worksheet.Cells[3, i].Value is null)
{
Debug.LogError($"当前{worksheet}中第3行第{i}个单元格数据为空");
return null;
}
string field = worksheet.Cells[3, i].Value.ToString();
fieldType.Add(field);
}
//取实际数据
for (int k = 4; k <= worksheet.Dimension.End.Row; k++)
{
List<string> realData = new List<string>();
for (int j = 1; j <= worksheet.Dimension.End.Column; j++)
{
if (worksheet.Cells[k, j].Value is null)
{
Debug.LogError($"当前{worksheet}中第{k}行第{j}个单元格数据为空");
return null;
}
string data = worksheet.Cells[k, j].Value.ToString();
realData.Add(data);
}
dataDic[k] = realData;
}
}
list.Add((scriptName, fieldType, fieldName, dataDic));
}
return list;
}
/// <summary>
/// 判断字符串列表中是否包含重复成员,有,返回true
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
private static bool HasRepeatedMember(List<string> list)
{
//往HaseSet中添加字符串,若不成功添加,说明有重复字符串
HashSet<string> hashSet = new HashSet<string>();
for (int i = 0; i < list.Count; i++)
{
if (!hashSet.Add(list[i]))
{
return true;
}
}
return false;
}
/// <summary>
/// 创建一个C#脚本
/// </summary>
/// <param name="path">脚本保存路径</param>
/// <param name="scriptName">脚本名</param>
/// <param name="nameSpaceList">命名空间列表</param>
/// <param name="fieldType">字段类型列表</param>
/// <param name="fieldName">字段名列表</param>
private static void CreateAScript(string path, string scriptName, List<string> nameSpaceList,
List<string> fieldType, List<string> fieldName)
{
#region 安全校验
if (fieldType is null || fieldType.Count == 0)
{
Debug.LogError($"{scriptName}字段类型列表错误");
return;
}
if (fieldName is null || fieldName.Count == 0)
{
Debug.LogError($"{scriptName}字段名列表错误");
return;
}
if (nameSpaceList is null || nameSpaceList.Count == 0)
{
Debug.LogError($"{scriptName}命名空间列表错误");
return;
}
if (fieldType.Count != fieldName.Count)
{
Debug.LogError($"{scriptName}字段类型列表与字段名列表长度不一致");
return;
}
//生成的字段以字母开头
for (int i = 0; i < fieldName.Count; i++)
{
if (!Regex.IsMatch(fieldName[i], @"^[a-zA-Z_]"))
{
Debug.LogError($"{scriptName}中字段名应以字母开头");
return;
}
}
//避免生成字段重复
if (HasRepeatedMember(fieldName))
{
Debug.LogError($"{scriptName}中出现重复字段");
return;
}
#endregion
using (StreamWriter writer = new StreamWriter(path))
{
//写入命名空间
for (int i = 0; i < nameSpaceList.Count; i++)
{
writer.WriteLine(nameSpaceList[i]);
}
//写入脚本名
writer.WriteLine($"public class {scriptName}");
writer.WriteLine("{");
//写入类型及字段
for (int i = 0; i < fieldName.Count; i++)
{
writer.WriteLine($"public {fieldType[i]} {fieldName[i]} ;");
}
writer.WriteLine("}");
}
}
/// <summary>
/// 创建json文件
/// </summary>
/// <param name="jsonName">json文件名</param>;
/// <param name="fieldType">字段类型列表</param>
/// <param name="fieldName">字段名列表</param>
/// <param name="dataDic">实际数据字典</param>
private static void CreateAJson(string jsonName, List<string> fieldType, List<string> fieldName, Dictionary<int, List<string>> dataDic)
{
//写一行数据
StringBuilder sb = new StringBuilder();
sb.Append("[\n");
for (int i = 4; i < dataDic.Count + 4; i++)
{
//写一行 追加
string s = GetALine(fieldType, fieldName, dataDic[i]);
sb.Append(s);
//不是最后一项数据,加逗号
if (i != dataDic.Count + 3)
{
sb.Append(",");
}
//换行
sb.Append("\n");
}
//写最后一个括号
sb.Append("]\n");
string path = jsonFolder + "/" + jsonName + ".json";
using (StreamWriter writer = new StreamWriter(path))
{
writer.WriteLine(sb.ToString());
}
}
/// <summary>
/// 将一行excel转为一行json
/// </summary>
/// <param name="fieldType">字段类型列表</param>
/// <param name="fieldName">字段名列表</param>
/// <param name="dataList">实际一行数据</param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private static string GetALine(List<string> fieldType, List<string> fieldName, List<string> dataList)
{
StringBuilder sb = new StringBuilder();
//写括号
sb.Append("{");
//遍历列表
for (int i = 0; i < fieldType.Count; i++)
{
//写入主键
string key = fieldName[i];
sb.Append($"\"{key}\":");
//写入值
string type = fieldType[i];
string value = dataList[i];
if (value is null)
{
throw new Exception("表格实际数据存在未配置项");
}
sb.Append($"{Convert(type, value)}");
//写入逗号
//不是最后一个就是逗号
if (i != fieldType.Count - 1)
{
sb.Append(",");
}
}
sb.Append("}");
return sb.ToString();
}
//根据类型获取键所对应的值
//如果不是数组 返回类型为 "Key":"Value" 中的value
//如果是数组 返回类似于 ["1","2"] 的结构
private static string Convert(string type, string value)
{
switch (type)
{
case "int":
case "float":
case "double":
case "bool":
case "string":
case "long":
//注此处返回的时候加了引号,避免格式错误
return $"\"{value}\"";
case "int[]":
case "float[]":
case "double[]":
case "bool[]":
case "string[]":
case "long[]":
return ArrayParse(value);
default:
throw new Exception("{type}类型暂未支持");
}
}
/// <summary>
/// 将数组转换成对应的字符串
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
private static string ArrayParse(string value)
{
//切分字符串得到数组
var res = value.Split("|");
StringBuilder sb = new StringBuilder();
sb.Append("[");
for (int i = 0; i < res.Length; i++)
{
sb.Append('"');
sb.Append(res[i]);
sb.Append('"');
//不是数组最后一个加,
if (i != res.Length - 1)
{
sb.Append(",");
}
}
sb.Append("]");
return sb.ToString();
}
}
直接获取该项目
总结
该工具可以帮助你轻松地将 .xlsx
文件中的数据转换为 JSON 文件,并自动生成对应的 C# 脚本,简化了数据处理和代码生成的流程。在使用时,务必遵循 Excel 文件格式要求,确保生成的脚本和 JSON 文件符合预期。