Excel转Json编辑器工具

功能说明:根据 .xlsx 文件生成对应的 JSON 文件,并自动创建脚本

注意事项

  1. Excel 读取依赖

    本功能依赖 EPPlus 库,只能读取 .xlsx 文件 。请确保将该脚本放置在 Assets 目录下的 Editor 文件夹中。同时,在 Editor 下再创建一个 Excel 目录,并将你的 .xlsx 文件放到 Excel 目录下。注意 :该目录下只能有一个 .xlsx 文件,且该文件是唯一的数据源。

    • Excel 文件格式要求
      • 第一行:字段名(与自动生成脚本中的字段对应)。
      • 第二行:中文注释。
      • 第三行:字段的数据类型(目前支持 intfloatdoublestringbool 和数组类型,如 int[]string[])。
      • 第四行开始:实际数据。
    • Epplus依赖获取查看我的另一篇文章Nuget For Unity插件介绍_nugetforunity-CSDN博客
  2. 生成脚本与 JSON 文件

    使用编辑器中的 ExcelTool 进行生成,点击 读取 Excel 按钮后,将自动执行以下操作:

    • 删除之前生成的脚本目录和 JSON 目录(如果存在),然后重新生成它们。
    • 注意:在这两个目录下请不要放置其他文件,因为此工具会在每次生成时覆盖这些目录。
  3. 关于数组格式

    数组数据需要按如下格式写入:
    例如1|2|3。数组成员使用 | 分隔。

  4. 支持多 sheet

    一个 .xlsx 文件中可以包含多个 sheet。在读取时,工具会读取所有 sheet 数据。请注意:

    • 将每个 sheet 的名字改为与要生成的脚本名一致。
    • Sheet 名称必须符合 C# 的命名规范,建议使用 TB_ 开头。

提示


格式

保证这个目录格式

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();
    }

}

直接获取该项目

ExcelToJson: ExcelToJson

总结

该工具可以帮助你轻松地将 .xlsx 文件中的数据转换为 JSON 文件,并自动生成对应的 C# 脚本,简化了数据处理和代码生成的流程。在使用时,务必遵循 Excel 文件格式要求,确保生成的脚本和 JSON 文件符合预期。

相关推荐
杜子腾dd3 小时前
14.使用各种读写包操作 Excel 文件:辅助模块
python·数据挖掘·excel·numpy·pandas·matplotlib
Macdo_cn3 小时前
Microsoft Excel 2024 LTSC mac v16.95 表格处理软件 支持M、Intel芯片
microsoft·macos·excel
进击的铁甲小宝4 小时前
使用 VLOOKUP 和条件格式在 Excel 中查找并标红匹配的串号
excel
石油人单挑所有8 小时前
Vim软件使用技巧
vim·excel
inxunoffice10 小时前
批量压缩与优化 Excel 文档,减少 Excel 文档大小
前端·excel
kse_music20 小时前
jsonl与json区别
json·jsonl
inxunoffice1 天前
批量将 Excel 文档中的图片提取到文件夹
excel
inxunoffice1 天前
批量在多个在 Excel 工作表的的指定位置插入新的 Sheet 工作表
excel
杜子腾dd1 天前
13. Pandas :使用 to_excel 方法写入 Excel文件
大数据·python·数据挖掘·excel·numpy·pandas
inxunoffice1 天前
批量删除或替换 Excel 的 Sheet 工作表
excel